summaryrefslogtreecommitdiff
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
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>
-rw-r--r--dhcpv6.h154
-rw-r--r--ldra.c221
2 files changed, 375 insertions, 0 deletions
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 <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);
+}
+