From 2bc7ba82d3c838352f8757c9f0454cc64af48cdb Mon Sep 17 00:00:00 2001 From: Bjørn Mork Date: Wed, 9 Nov 2011 16:07:21 +0100 Subject: Basic demo code, implementing a BPF to capture IPv6 udp 547 traffic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Still lacking most functionality, but at least capture is working Signed-off-by: Bjørn Mork --- ldra.c | 221 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 221 insertions(+) create mode 100644 ldra.c (limited to 'ldra.c') 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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); +} + -- cgit v1.2.3