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 --- dhcpv6.h | 154 ++++++++++++++++++++++++++++++++++++++++++++ ldra.c | 221 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 375 insertions(+) create mode 100644 dhcpv6.h create mode 100644 ldra.c diff --git a/dhcpv6.h b/dhcpv6.h new file mode 100644 index 0000000..1596034 --- /dev/null +++ b/dhcpv6.h @@ -0,0 +1,154 @@ +struct dhcpv6_msg { /* [RFC3315] ch 6 */ + u_int8_t msg_type; /* Message type */ + u_int8_t xid[3]; /* transaction-id */ + char options[]; /* options */ +}; + + +struct dhcpv6_relay_msg { /* [RFC3315] ch 7 */ + u_int8_t msg_type; /* Message type */ + u_int8_t hop_count; /* hop count */ + struct in6_addr link_addr; /* link address */ + struct in6_addr peer_addr; /* peer address */ + char options[]; /* options */ +}; + +struct dhcpv6_option { /* [RFC3315] generic option */ + u_int16_t code; /* option-code */ + u_int16_t len; /* option-len */ + char data[]; /* any */ +}; + +struct dhcpv6_interface_id { /* [RFC3315] ch 22.18 */ + u_int16_t code; /* option-code (18) */ + u_int16_t len; /* option-len */ + char id[]; /* interface-id */ +}; + +struct dhcpv6_remote_id { /* [RFC4649] */ + u_int16_t code; /* option-code (37) */ + u_int16_t len; /* option-len */ + u_int32_t enterprise; /* enterprise-number */ + char id[]; /* remote-id */ +}; + +struct dhcpv6_subscriber_id { /* [RFC4580] */ + u_int16_t code; /* option-code (38) */ + u_int16_t len; /* option-len */ + char id[]; /* subscriber-id */ +}; + +/* all the following are ref http://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xml */ + +/* message types */ +/* 0 Reserved */ +#define DHCPV6_SOLICIT 1 /* [RFC3315] */ +#define DHCPV6_ADVERTISE 2 /* [RFC3315] */ +#define DHCPV6_REQUEST 3 /* [RFC3315] */ +#define DHCPV6_CONFIRM 4 /* [RFC3315] */ +#define DHCPV6_RENEW 5 /* [RFC3315] */ +#define DHCPV6_REBIND 6 /* [RFC3315] */ +#define DHCPV6_REPLY 7 /* [RFC3315] */ +#define DHCPV6_RELEASE 8 /* [RFC3315] */ +#define DHCPV6_DECLINE 9 /* [RFC3315] */ +#define DHCPV6_RECONFIGURE 10 /* [RFC3315] */ +#define DHCPV6_INFORMATION_REQ 11 /* [RFC3315] */ +#define DHCPV6_RELAY_FORW 12 /* [RFC3315] */ +#define DHCPV6_RELAY_REPL 13 /* [RFC3315] */ +#define DHCPV6_LEASEQUERY 14 /* [RFC5007] */ +#define DHCPV6_LEASEQUERY_REPLY 15 /* [RFC5007] */ +#define DHCPV6_LEASEQUERY_DONE 16 /* [RFC5460] */ +#define DHCPV6_LEASEQUERY_DATA 17 /* [RFC5460] */ +/* 18-255 Unassigned */ + +/* option codes */ +/* 0 Reserved */ +#define OPTION_CLIENTID 1 /* [RFC3315] */ +#define OPTION_SERVERID 2 /* [RFC3315] */ +#define OPTION_IA_NA 3 /* [RFC3315] */ +#define OPTION_IA_TA 4 /* [RFC3315] */ +#define OPTION_IAADDR 5 /* [RFC3315] */ +#define OPTION_ORO 6 /* [RFC3315] */ +#define OPTION_PREFERENCE 7 /* [RFC3315] */ +#define OPTION_ELAPSED_TIME 8 /* [RFC3315] */ +#define OPTION_RELAY_MSG 9 /* [RFC3315] */ +/* 10 Unassigned */ +#define OPTION_AUTH 11 /* [RFC3315] */ +#define OPTION_UNICAST 12 /* [RFC3315] */ +#define OPTION_STATUS_CODE 13 /* [RFC3315] */ +#define OPTION_RAPID_COMMIT 14 /* [RFC3315] */ +#define OPTION_USER_CLASS 15 /* [RFC3315] */ +#define OPTION_VENDOR_CLASS 16 /* [RFC3315] */ +#define OPTION_VENDOR_OPTS 17 /* [RFC3315] */ +#define OPTION_INTERFACE_ID 18 /* [RFC3315] */ +#define OPTION_RECONF_MSG 19 /* [RFC3315] */ +#define OPTION_RECONF_ACCEPT 20 /* [RFC3315] */ +/* 21 SIP Servers Domain Name List [RFC3319] */ +/* 22 SIP Servers IPv6 Address List [RFC3319] */ +/* 23 DNS Recursive Name Server Option [RFC3646] */ +/* 24 Domain Search List option [RFC3646] */ +#define OPTION_IA_PD 25 /* [RFC3633] */ +#define OPTION_IAPREFIX 26 /* [RFC3633] */ +#define OPTION_NIS_SERVERS 27 /* [RFC3898] */ +#define OPTION_NISP_SERVERS 28 /* [RFC3898] */ +#define OPTION_NIS_DOMAIN_NAME 29 /* [RFC3898] */ +#define OPTION_NISP_DOMAIN_NAME 30 /* [RFC3898] */ +#define OPTION_SNTP_SERVERS 31 /* [RFC4075] */ +#define OPTION_INFORMATION_REFRESH_TIME 32 /* [RFC4242] */ +#define OPTION_BCMCS_SERVER_D 33 /* [RFC4280] */ +#define OPTION_BCMCS_SERVER_A 34 /* [RFC4280] */ +/* 35 Unassigned */ +#define OPTION_GEOCONF_CIVIC 36 /* [RFC4776] */ +#define OPTION_REMOTE_ID 37 /* [RFC4649] */ +#define OPTION_SUBSCRIBER_ID 38 /* [RFC4580] */ +#define OPTION_CLIENT_FQDN 39 /* [RFC4704] */ +#define OPTION_PANA_AGENT 40 /* [RFC5192] */ +#define OPTION_NEW_POSIX_TIMEZONE 41 /* [RFC4833] */ +#define OPTION_NEW_TZDB_TIMEZONE 42 /* [RFC4833] */ +#define OPTION_ERO 43 /* [RFC4994] */ +#define OPTION_LQ_QUERY 44 /* [RFC5007] */ +#define OPTION_CLIENT_DATA 45 /* [RFC5007] */ +#define OPTION_CLT_TIME 46 /* [RFC5007] */ +#define OPTION_LQ_RELAY_DATA 47 /* [RFC5007] */ +#define OPTION_LQ_CLIENT_LINK 48 /* [RFC5007] */ +#define OPTION_MIP6_HNINF 49 /* [RFC-ietf-mip6-hiopt-17] */ +#define OPTION_MIP6_RELAY 50 /* [RFC-ietf-mip6-hiopt-17] */ +#define OPTION_V6_LOST 51 /* [RFC5223] */ +#define OPTION_CAPWAP_AC_V6 52 /* [RFC5417] */ +#define OPTION_RELAY_ID 53 /* [RFC5460] */ +#define OPTION_IPv6_Address_MoS 54 /* [RFC5678] */ +#define OPTION_IPv6_FQDN_MoS 55 /* [RFC5678] */ +#define OPTION_NTP_SERVER 56 /* [RFC5908] */ +#define OPTION_V6_ACCESS_DOMAIN 57 /* [RFC5986] */ +#define OPTION_SIP_UA_CS_LIST 58 /* [RFC6011] */ +#define OPT_BOOTFILE_URL 59 /* [RFC5970] */ +#define OPT_BOOTFILE_PARAM 60 /* [RFC5970] */ +#define OPTION_CLIENT_ARCH_TYPE 61 /* [RFC5970] */ +#define OPTION_NII 62 /* [RFC5970] */ +#define OPTION_GEOLOCATION 63 /* [RFC6225] */ +#define OPTION_AFTR_NAME 64 /* [RFC6334] */ +#define OPTION_ERP_LOCAL_DOMAIN_NAME 65 /* [RFC-ietf-hokey-ldn-discovery-10] */ +#define OPTION_RSOO 66 /* [RFC-ietf-dhc-dhcpv6-relay-supplied-options-09] */ +/* 67-255 Unassigned */ + +/* Status Codes */ +#define Success 0 /* [RFC3315] */ +#define UnspecFail 1 /* [RFC3315] */ +#define NoAddrsAvail 2 /* [RFC3315] */ +#define NoBinding 3 /* [RFC3315] */ +#define NotOnLink 4 /* [RFC3315] */ +#define UseMulticast 5 /* [RFC3315] */ +#define NoPrefixAvail 6 /* [RFC3633] */ +#define UnknownQueryType 7 /* [RFC5007] */ +#define MalformedQuery 8 /* [RFC5007] */ +#define NotConfigured 9 /* [RFC5007] */ +#define NotAllowed 10 /* [RFC5007] */ +#define QueryTerminated 11 /* [RFC5460] */ +/* 12-255 Unassigned */ + + +/* DUID types */ +#define DUID_LLT 1 /* [RFC3315] */ +#define DUID_EN 2 /* [RFC3315] */ +#define DUID_LL 3 /* [RFC3315] */ +#define DUID_UUID 4 /* [RFC6355] */ 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