summaryrefslogtreecommitdiff
path: root/ldra.c
diff options
context:
space:
mode:
authorBjørn Mork <bjorn@mork.no>2011-11-09 16:07:21 +0100
committerBjørn Mork <bjorn@mork.no>2011-11-09 16:20:37 +0100
commit2bc7ba82d3c838352f8757c9f0454cc64af48cdb (patch)
tree801b0dfaff00cced4aaddae21a5f520c5e42907d /ldra.c
parent7be3f0592841640c07819aa23f14735de9f767d5 (diff)
Basic demo code, implementing a BPF to capture IPv6 udp 547 traffic
Still lacking most functionality, but at least capture is working Signed-off-by: Bjørn Mork <bjorn@mork.no>
Diffstat (limited to 'ldra.c')
-rw-r--r--ldra.c221
1 files changed, 221 insertions, 0 deletions
diff --git a/ldra.c b/ldra.c
new file mode 100644
index 0000000..2062c28
--- /dev/null
+++ b/ldra.c
@@ -0,0 +1,221 @@
+
+/*
+ client facing interfaces:
+ ifid, remoteid, subscriberid , trusted
+
+
+ intercept traffic to ff02::1:2 udp port 547
+
+ drop these message types:
+
+ o ADVERTISE (2)
+
+ o REPLY (7)
+
+ o RECONFIGURE (10)
+
+ o RELAY-REPL (13)
+
+ o RELAY-FORW (12) unless trusted
+
+
+ copy ll and ip source and destination
+ create RELAY-FORW
+
+
+ network facing interfaces:
+
+ intercept traffic from link local to link local udp dest port 547
+
+ drop any message except
+
+ o RELAY-REPL (13)
+
+
+ generic sanity:
+
+ If a Relay-Message would exceed the MTU of the outgoing interface, it
+ MUST be discarded, and an error condition SHOULD be logged.
+
+
+
+ code sample:
+http://aschauf.landshut.org/fh/linux/udp_vs_raw/ch01s03.html
+
+ */
+
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netpacket/packet.h>
+#include <linux/if_ether.h>
+#include <linux/filter.h>
+#include <netinet/in.h>
+#include <netinet/ip6.h>
+#include <netinet/udp.h>
+#include "dhcpv6.h"
+
+/* attempting to filter out IPv6 to udp port 547
+ NOTE: This filter is created as short as possible for sorting out
+ the interesting packets. The packets need further sanity checking
+ after matching this. Addresses, source ports and DHCPv6 message
+ types should be verified later.
+ */
+static struct sock_filter ipv6udp547[] = {
+ /* verify IPv6 */
+ BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 12), /* 6 byte dst + 6 byte src */
+ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ETH_P_IPV6, 0, 5), /* 86dd */
+
+ /* verify next header = udp (NOTE: ingoring any additional headers!) */
+ BPF_STMT(BPF_LD+BPF_B+BPF_ABS, 20), /* 14 byte ethernet + 4 byte ipver/class/flow + 2byte payload len */
+ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, IPPROTO_UDP, 0, 3), /* udp = 17 */
+
+ /* src IPv6 addr = 22, dst IPv6 addr = 38 - do not check these here */
+ /* verify destination port */
+ BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 56), /* 14 byte ethernet + 40 byte IPv6 + 2 byte src port */
+ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 547, 0, 1),
+
+ BPF_STMT(BPF_RET+BPF_K, (u_int)-1),
+ BPF_STMT(BPF_RET+BPF_K, 0),
+};
+
+/* TODO: create an alternate VLAN matching filter to allow us to use VLAN tags as interface IDs? */
+
+int print_mac(const char *mac) {
+ fprintf(stderr, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
+ mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+}
+
+int print_hex(const char *buf, size_t len) {
+ char *p;
+ unsigned int i = 0;
+
+ for (p = (char *)buf; p < (buf + len); p++) {
+ if (i % 16 == 0)
+ fprintf(stderr, "\n%04x", i);
+ if (i % 8 == 0)
+ fprintf(stderr, " ");
+ fprintf(stderr, " %02hhx", *p);
+ i++;
+ }
+ fprintf(stderr, "\n\n");
+}
+
+int main(int argc, char *argv[]) {
+ int domain, s, i;
+ char str[INET6_ADDRSTRLEN];
+ struct sockaddr_ll ll;
+ char *buf;
+ int fromlen, len = 0;
+ struct sock_fprog fprog;
+
+ struct ethhdr *eth;
+ struct ip6_hdr *ip6;
+ struct udphdr *udp;
+ struct dhcpv6_msg *dhcpv6;
+
+ s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); /* use ETH_P_IPV6 ? */
+ if (s == -1) {
+ fprintf(stderr, "%s(): socket failed: %m\n", __FUNCTION__);
+ return(0);
+ }
+
+/* from packet(7):
+
+ By default all packets of the specified protocol type are passed to a packet
+ socket. To only get packets from a specific interface use bind(2) specifying
+ an address in a struct sockaddr_ll to bind the packet socket to an interface.
+ Only the sll_protocol and the sll_ifindex address fields are used for purposes
+ of binding.
+
+ from socket(7):
+
+BUGS
+ The CONFIG_FILTER socket options SO_ATTACH_FILTER and SO_DETACH_FILTER are not documented. The suggested
+ interface to use them is via the libpcap library.
+
+ from linux/Documentation/networking/filter.txt :
+
+setsockopt(sockfd, SOL_SOCKET, SO_ATTACH_FILTER, &Filter, sizeof(Filter));
+setsockopt(sockfd, SOL_SOCKET, SO_DETACH_FILTER, &value, sizeof(value));
+
+See the BSD bpf.4 manpage and the BSD Packet Filter paper written by
+Steven McCanne and Van Jacobson of Lawrence Berkeley Laboratory.
+
+
+
+ see also linux/net/core/filter.c
+
+ and /usr/include/linux/filter.h
+*/
+
+ fprog.len = sizeof(ipv6udp547)/sizeof(struct sock_filter);
+ fprog.filter = ipv6udp547;
+
+ setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
+
+ fromlen = sizeof(ll);
+ buf = malloc(ETH_FRAME_LEN);
+ len = recvfrom(s, buf, ETH_FRAME_LEN, 0, (struct sockaddr *)&ll, &fromlen);
+ if (len == -1) {
+ fprintf(stderr, "%s(): recvfrom failed: %m\n", __FUNCTION__);
+ return(0);
+ }
+
+ fprintf(stderr, "%s(): received %d bytes from address with len=%d\n", __FUNCTION__, len, ll.sll_halen );
+ print_mac((char *)ll.sll_addr);
+ fprintf(stderr,"\n");
+
+ print_hex(buf, len);
+
+
+ /* TODO:
+ verify that packet arrived on one of the interfaces we're wathcing,
+ and decide whether it is going up or down based on interface.
+
+ up:
+ verify that destination address is ff02::1:2
+ verify that source address is link local
+ verify that source port is 546
+ verify that message type is not one of the forbidden ones
+
+ get hop count from original packet if type is RELAY-FORW
+ and interface is trusted
+
+ insert new options, including DHCPPACKET attribute between
+ udp header and original DHCP packet
+
+ outgoing interface is the predefined upstream interface
+
+
+ down:
+ verify that destination and source address is link local
+ verify that source port is 547
+ verify that message type is RELAY-REPL (13)
+
+ strip away the outer RELAY envelope, using the interface-id to select outgoing interface
+
+ common code:
+
+ fixup both IPv6 payload length and UDP length
+ verify that the new length doesn't exceed outgoing interface MTU (or log error)
+ fixup UDP checksum
+ transmit on the selected outgoing interface (FIMXE: support multiple upstream interfaces?)
+
+
+
+ */
+
+ eth = (struct ethhdr *)buf;
+ ip6 = (struct ip6_hdr *)(buf + sizeof(struct ethhdr));
+ udp = (struct udphdr *)(buf + sizeof(struct ethhdr) + sizeof(struct ip6_hdr));
+ dhcpv6 = (struct dhcpv6_msg *)(buf + sizeof(struct ethhdr) + sizeof(struct ip6_hdr) + sizeof(struct udphdr));
+
+ fprintf(stderr,"ip6.ip6_plen=%hd, udp.len=%hd, dhcpv6.msg_type=%hhd\n",
+ ntohs(ip6->ip6_plen), ntohs(udp->len), dhcpv6->msg_type);
+
+ return(0);
+}
+