From 6739e13c64fd2e9a4796cab6fa9c99a2d6134428 Mon Sep 17 00:00:00 2001 From: Nicholas DeMarinis Date: Fri, 29 Sep 2023 09:35:23 -0400 Subject: Added vnet_scripts, RIP dissector, .gitattributes. --- util/rip_dissector/README.md | 70 ++++++++++++++++++++++++ util/rip_dissector/cs168_rip.lua | 115 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 185 insertions(+) create mode 100644 util/rip_dissector/README.md create mode 100644 util/rip_dissector/cs168_rip.lua (limited to 'util/rip_dissector') diff --git a/util/rip_dissector/README.md b/util/rip_dissector/README.md new file mode 100644 index 0000000..abfd6a5 --- /dev/null +++ b/util/rip_dissector/README.md @@ -0,0 +1,70 @@ +# CS168 RIP Dissector + +This directory contains a dissector (also known as a decoder) for the +RIP protocol implementation for CS168. + +## Installation Instructions + +The dissector is provided as a Lua script in this directory. For +security reasons, Wireshark does not run Lua scripts when run as +root--therefore, you must ensure that you are using Wireshark as your +local user, not with root or sudo. To run wireshark as a standard +user, make sure your user is added to the `wireshark` group. If you +are using the provided VM, the the vagrant user is already in the +wireshark group. However, if you are running Wireshark on your own +system, you will need to configure this yourself. + +Once you have Wireshark running as your user. Add the dissector to +Wireshark, by copying the script into your plugins directory. + +To do this: + 0. Run wireshark as your user (**not with root or sudo**). + 1. Open Wireshark's Help menu and select "About Wireshark". + 2. In the folders tab, find the entry "Personal Lua Plugins". For + example: `~/.config/wireshark/plugins` + 3. Copy the script to this directory (if it doesn't exist, create it) + and restart wireshark + 4. Open the "About Wireshark" window again and look in the Plugins + tab. You should now see cs168_rip.lua in the list of plugins. + +## Using the dissector + +Note: To make sure your dissector is working, please run the reference IP +node with an example network to ensure you are testing with correct +packets. + +Wireshark will automatically invoke the RIP dissector when it +encounters an IP packet using protocol number 200. + +Four our overlay network, however, Wireshark does not automatically +know to interpret our IP-in-UDP packets as IP packets. You can tell +wireshark to do this using its "User-specified decodes" feature: + 1. Start capturing traffic for the IP assignment. In most cases, + you will be capturing on the loopback interface. + 2. Find a UDP packet related to the assignment and select it. These + packets will use the port numbers specified in the lnx files, and + therefore may be different depending on the network you are running. + 3. Look in the lower pane that shows the layers present in ths + packet. Under UDP, you should see a layer "Data" that contains + our Virtual IP packets. Select this field. + 4. Right-click on the field and select "**Decode As...**" This should + open a window and add a rule template to decode UDP traffic on the port + number used in the packet. In the rightmost column ("Current"), + select the IPv4 decoder, then click "**Save**". + 5. Wireshark should now update and decode the UDP packets first as + IP packets, and then decode those with protocol 200 as RIP using + the dissector you installed. + +If you do not see IP packets encapsulated in your UDP packets, check +your "Decode as... rules from Step 5. If you do not see RIP being +decoded, make sure the plugin is loaded in the help menu. + +**Note**: this will only invoke the correct decoder on a single UDP +port. If you want to decode the traffic for multiple nodes, repeat +this process for each port you need to observe. + +## Feedback + +This decoder and the instructions are new. If you have questions or +encounter any issues, please post on Piazza or see the course staff +for help. diff --git a/util/rip_dissector/cs168_rip.lua b/util/rip_dissector/cs168_rip.lua new file mode 100644 index 0000000..4e6e6d9 --- /dev/null +++ b/util/rip_dissector/cs168_rip.lua @@ -0,0 +1,115 @@ +-- CS168 RIP Protocol Dissector +-- +-- The structure of a RIP message is as follows: +-- uint16_t command +-- uint16_t num_entries +-- struct { +-- uint32_t cost +-- uint32_t address +-- } entries[num_entries] + +local RIP_HEADER_LEN = 4 +local ROUTE_ENTRY_LEN = 12 + +rip_protocol = Proto("CS168RIP", "CS168 RIP") + +command = ProtoField.uint16("cs168rip.command", "command", base.DEC) +num_entries = ProtoField.uint16("cs168rip.num_entries", "num_entries", base.DEC) + +rip_entry_cost = ProtoField.uint32("cs168rip.entry.cost", "cost", base.DEC) +rip_entry_addr = ProtoField.ipv4("cs168rip.entry.address", "address") +rip_entry_mask = ProtoField.ipv4("cs168rip.entry.mask", "mask") + + +rip_protocol.fields = { + command, + num_entries, + rip_entry_cost, + rip_entry_addr, + rip_entry_mask, +} + +local ef_bad_entry = ProtoExpert.new("cs168rip.query.entry.expert", + "Route entry missing or malformed", + expert.group.MALFORMED, + expert.severity.WARN) + +rip_protocol.experts = { + ef_bad_entry, +} + +local field_cost = Field.new("cs168rip.entry.cost") +local field_addr = Field.new("cs168rip.entry.address") +local field_mask = Field.new("cs168rip.entry.mask") + + +function rip_protocol.dissector(buffer, pinfo, tree) + length = buffer:len() + if length == 0 then return end + + pinfo.cols.protocol = rip_protocol.name + + local subtree = tree:add(rip_protocol, buffer(), "CS168 RIP Protocol") + + local pktlen = buffer:reported_length_remaining() + + local cmd_num = buffer(0, 2):uint() + local cmd_name = get_command_name(cmd_num) + + -- Add command ID and name + subtree:add(command, buffer(0, 2)):append_text(" (" .. cmd_name .. ") ") + pinfo.cols.info:append("RIP " .. cmd_name) + + -- num_entries + local entry_count = buffer(2, 2):uint() + subtree:add(num_entries, buffer(2, 2)) + pinfo.cols.info:append(" (" .. entry_count .. " entries)") + + local pos = RIP_HEADER_LEN + local e_idx = 0 + + -- Parse each entry + if entry_count > 0 then + local entry_tree = subtree:add("Entries") + + local pkt_remaining = pktlen - pos + + while entry_count > 0 and pkt_remaining > 0 do + if pkt_remaining < ROUTE_ENTRY_LEN then + entry_tree:add_proto_expert_info(ef_bad_entry) + return + end + + local cost = buffer(pos, 4):uint() + local address = buffer(pos + 4, 4):uint() + local mask = buffer(pos + 8, 4):uint() + + -- TODO Add each entry to its own subtree (with helpful summary) + local etree = entry_tree + + etree:add(rip_entry_cost, buffer(pos, 4)) + etree:add(rip_entry_addr, buffer(pos + 4, 4)) + etree:add(rip_entry_mask, buffer(pos + 8, 4)) + + pos = pos + 12 + + pkt_remaining = pkt_remaining - 12 + entry_count = entry_count - 1 + e_idx = e_idx + 1 + end + end + +end + +function get_command_name(cmd) + local name = "UNKNOWN" + if cmd == 1 then name = "REQUEST" + elseif cmd == 2 then name = "RESPONSE" + end + + return name +end + + +local ip_proto = DissectorTable.get("ip.proto") +ip_proto:add(200, rip_protocol.dissector) -- cgit v1.2.3-70-g09d2