diff --git a/dhcp-relay/dhcp-relay.h b/dhcp-relay/dhcp-relay.h index 2a0c6ed..ef85a02 100644 --- a/dhcp-relay/dhcp-relay.h +++ b/dhcp-relay/dhcp-relay.h @@ -21,7 +21,7 @@ struct sub_option { __u8 option_id; __u8 len; - __u16 val; + char val[IF_NAMESIZE]; }; /*structure for dhcp option 82 */ diff --git a/dhcp-relay/dhcp_kern_xdp.c b/dhcp-relay/dhcp_kern_xdp.c index 281088c..099eb84 100644 --- a/dhcp-relay/dhcp_kern_xdp.c +++ b/dhcp-relay/dhcp_kern_xdp.c @@ -2,6 +2,7 @@ #include #include +#include /* IF_NAMESIZE */ #include #include #include @@ -23,22 +24,48 @@ struct { __uint(max_entries, 3); } relay_config SEC(".maps"); +/* + * This map is for storing the device name in clear text. + * Device name is used for DHCP option 82. + */ + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __type(key, __u32); + __type(value, char[IF_NAMESIZE]); + __uint(max_entries, 1); +} device_name SEC(".maps"); + +/* + * This map is used for storing client requests along with their matching + * VLAN tags. That way, we can handle DHCP server replies. + * Client MAC address is used as key, VLAN headers as value. + */ + +struct { + __uint(type, BPF_MAP_TYPE_LRU_HASH); + __type(key, __u64); + __type(value, struct collect_vlans); + __uint(max_entries, 16384); +} client_vlans SEC(".maps"); + /* Inserts DHCP option 82 into the received dhcp packet * at the specified offset. */ static __always_inline int write_dhcp_option_82(void *ctx, int offset, - struct collect_vlans *vlans) { + struct collect_vlans *vlans, char *dev) { struct dhcp_option_82 option; option.t = DHO_DHCP_AGENT_OPTIONS; - option.len = 8; + option.len = sizeof(struct sub_option) + sizeof(struct sub_option); option.circuit_id.option_id = RAI_CIRCUIT_ID; - option.circuit_id.len = RAI_OPTION_LEN; - option.circuit_id.val = bpf_ntohs(vlans->id[0]); + option.circuit_id.len = IF_NAMESIZE; + memcpy(option.circuit_id.val, dev, IF_NAMESIZE); + //option.circuit_id.val = bpf_ntohs(vlans->id[0]); option.remote_id.option_id = RAI_REMOTE_ID; - option.remote_id.len = RAI_OPTION_LEN; - option.remote_id.val = bpf_ntohs(vlans->id[1]); - + option.remote_id.len = IF_NAMESIZE; + //option.remote_id.val = bpf_ntohs(vlans->id[1]); + return xdp_store_bytes(ctx, offset, &option, sizeof (option), 0); } @@ -104,9 +131,8 @@ int xdp_dhcp_relay(struct xdp_md *ctx) { bpf_printk("Cannot tail extend packet, delta %i - error code %i", delta, res); return XDP_ABORTED; } - - //bpf_printk("static_offset %i (hex %x)", static_offset, static_offset); - //bpf_printk("dhcp_offset %i (hex %x)", dhcp_offset, dhcp_offset); + + bpf_printk("Tail extended packet by %i bytes", delta); void *data_end = (void *) (long) ctx->data_end; void *data = (void *) (long) ctx->data; @@ -126,6 +152,8 @@ int xdp_dhcp_relay(struct xdp_md *ctx) { __u16 vlan_length = 0; __u8 option_code = 0; __u8 option_length = 0; + __u64 client_mac = 0; + char *dev; int i = 0; /* These keep track of the next header type and iterator pointer */ @@ -177,6 +205,12 @@ int xdp_dhcp_relay(struct xdp_md *ctx) { if (udp->dest != bpf_htons(DHCP_SERVER_PORT) && udp->dest != bpf_htons(DHCP_CLIENT_PORT)) goto out; + /* Increase IP length header */ + ip->tot_len += bpf_htons(delta); + + /* Increase UDP length header */ + udp->len += bpf_htons(delta); + /* Read DHCP server IP from config map */ key = 0; dhcp_srv_ip = bpf_map_lookup_elem(&relay_config, &key); @@ -194,6 +228,12 @@ int xdp_dhcp_relay(struct xdp_md *ctx) { relay_hwaddr = bpf_map_lookup_elem(&relay_config, &key); if (relay_hwaddr == NULL) goto out; + + /* Read device name from device map */ + key = 0; + dev = bpf_map_lookup_elem(&device_name, &key); + if (dev == NULL) + goto out; /* Copy headers of packet to buf */ //if (xdp_load_bytes(ctx, 0, buf, static_offset)) @@ -223,6 +263,12 @@ int xdp_dhcp_relay(struct xdp_md *ctx) { } dhcp = data + vlan_length + dhcp_offset; + /* Store client MAC */ + if (dhcp->chaddr + ETH_ALEN > data_end) { + goto out; + } + memcpy(&client_mac, dhcp->chaddr, ETH_ALEN); + bpf_printk("Parsing DHCP packet, opcode %i, hops %i", dhcp->op, dhcp->hops); if (dhcp->op == DHCP_REQUEST && (eth->h_dest[0] == 0xff @@ -243,12 +289,18 @@ int xdp_dhcp_relay(struct xdp_md *ctx) { //memcpy(eth->h_source, relay_hwaddr, ETH_ALEN); // Set GIADDR - if(&dhcp->giaddr.s_addr + sizeof(relay_agent_ip) > data_end) { + if (&dhcp->giaddr.s_addr + sizeof (relay_agent_ip) > data_end) { rc = XDP_ABORTED; goto out; } dhcp->giaddr.s_addr = *relay_agent_ip; + /* Save client VLAN in state map */ + if (bpf_map_update_elem(&client_vlans, &client_mac, &vlans, BPF_ANY)) { + bpf_printk("Could not save DHCP request in state map"); + goto out; + } + } else if (dhcp->op == DHCP_REPLY && (eth->h_dest[0] != 0xff || eth->h_dest[1] != 0xff @@ -270,6 +322,15 @@ int xdp_dhcp_relay(struct xdp_md *ctx) { * to the end user */ + struct collect_vlans *new_vlans; + new_vlans = bpf_map_lookup_elem(&client_vlans, &client_mac); + if (new_vlans == NULL) { + bpf_printk("Could not find map entry for MAC %i", client_mac); + goto out; + } + + bpf_printk("Found map entry for MAC %i", client_mac); + } /* Check hops */ @@ -307,7 +368,7 @@ int xdp_dhcp_relay(struct xdp_md *ctx) { bpf_printk("Going to write DHCP option at offset %i", option_offset); /* Insert Option 82 before END option */ - if (write_dhcp_option_82(ctx, option_offset, &vlans)) { + if (write_dhcp_option_82(ctx, option_offset, &vlans, dev)) { bpf_printk("Could not write DHCP option 82 at offset %i", option_offset); return XDP_ABORTED; } @@ -325,6 +386,8 @@ int xdp_dhcp_relay(struct xdp_md *ctx) { bpf_printk("Could not write DHCP option 255 at offset %i", option_offset); return XDP_ABORTED; } + + bpf_printk("Wrote DHCP option 255 at offset %i, returning XDP_PASS", option_offset); break; } @@ -384,8 +447,6 @@ int xdp_dhcp_relay(struct xdp_md *ctx) { ip->check = ~sum; rc = XDP_PASS; - bpf_printk("Wrote DHCP option at offset %i, returning XDP_PASS", offset); - goto out; out: diff --git a/dhcp-relay/dhcp_user_xdp.c b/dhcp-relay/dhcp_user_xdp.c index 26afd91..efe2a21 100644 --- a/dhcp-relay/dhcp_user_xdp.c +++ b/dhcp-relay/dhcp_user_xdp.c @@ -17,6 +17,7 @@ #include #define SERVER_MAP "relay_config" +#define DEVICE_MAP "device_name" #define XDP_OBJ "dhcp_kern_xdp.o" static const struct option options[] = { @@ -102,8 +103,10 @@ int main(int argc, char **argv) { char dev[IF_NAMESIZE] = ""; bool do_unload = 0; struct bpf_map *map = NULL; + struct bpf_map *device_map = NULL; struct bpf_object *obj = NULL; int map_fd; + int device_map_fd; int key = 0; struct in_addr dhcp_server_addr = {}; struct in_addr relay_agent_addr = {}; @@ -197,7 +200,7 @@ int main(int argc, char **argv) { memcpy(&hwaddr, (unsigned char *) ifr.ifr_hwaddr.sa_data, 6); //display mac address - printf("Mac : %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + printf("Using device %s MAC: %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", dev, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); /* Load the BPF-ELF object file and get back first BPF_prog FD */ err = bpf_prog_load(filename, BPF_PROG_TYPE_XDP, &obj, &prog_fd); @@ -255,6 +258,33 @@ int main(int argc, char **argv) { exit(-1); } + + /* read the map from prog object file and update the real + * server IP to the map + */ + device_map = bpf_object__find_map_by_name(obj, DEVICE_MAP); + err = libbpf_get_error(device_map); + if (err) { + fprintf(stderr, "Could not find map %s in %s: %s\n", DEVICE_MAP, + XDP_OBJ, strerror(err)); + device_map = NULL; + exit(-1); + } + device_map_fd = bpf_map__fd(device_map); + if (device_map_fd < 0) { + fprintf(stderr, "Could not get device map fd\n"); + exit(-1); + } + + // Set device name in map + key = 0; + err = bpf_map_update_elem(device_map_fd, &key, dev, BPF_ANY); + if (err) { + fprintf(stderr, "Could not update map %s in %s\n", DEVICE_MAP, + XDP_OBJ); + exit(-1); + } + err = xdp_link_attach(ifindex, xdp_flags, prog_fd); if (err) return err;