From a9003fa2660b264e4a8b8516c173f46cbe847aff Mon Sep 17 00:00:00 2001 From: Yoel Caspersen Date: Mon, 18 Oct 2021 09:20:16 +0200 Subject: [PATCH] Reformatting option 82 circuit ID to include interface name (including VLAN tags) in ASCII format. Because VLAN tags are converted to ASCII with use of modulo, interface name is generated from right to left, starting with inner VLAN and prepended with dash characters to ensure a final length of IF_NAMESIZE bytes (16), e.g.: ----ens6f0.83.20 We avoid null bytes in circuit ID to ensure compatibility with DHCP servers that interpret null as string terminator. --- dhcp-relay/dhcp-relay.h | 3 +- dhcp-relay/dhcp_kern_xdp.c | 84 +++++++++++++++++++++++++++++++------- dhcp-relay/dhcp_user_xdp.c | 61 ++++++++++++++++++++++++--- 3 files changed, 127 insertions(+), 21 deletions(-) diff --git a/dhcp-relay/dhcp-relay.h b/dhcp-relay/dhcp-relay.h index ef85a02..09bca44 100644 --- a/dhcp-relay/dhcp-relay.h +++ b/dhcp-relay/dhcp-relay.h @@ -10,7 +10,8 @@ #define DHO_DHCP_AGENT_OPTIONS 82 #define RAI_CIRCUIT_ID 1 #define RAI_REMOTE_ID 2 -#define RAI_OPTION_LEN 2 +#define RAI_OPTION_LEN 40 +#define VLAN_ASCII_MAX 4 /* Max bytes needed to store VLAN in ASCII format */ #define DHCP_SERVER_PORT 67 #define DHCP_CLIENT_PORT 68 diff --git a/dhcp-relay/dhcp_kern_xdp.c b/dhcp-relay/dhcp_kern_xdp.c index 099eb84..56cd040 100644 --- a/dhcp-relay/dhcp_kern_xdp.c +++ b/dhcp-relay/dhcp_kern_xdp.c @@ -2,7 +2,7 @@ #include #include -#include /* IF_NAMESIZE */ +#include /* IF_NAMESIZE */ #include #include #include @@ -49,7 +49,7 @@ struct { __uint(max_entries, 16384); } client_vlans SEC(".maps"); -/* Inserts DHCP option 82 into the received dhcp packet +/* 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, @@ -57,14 +57,68 @@ static __always_inline int write_dhcp_option_82(void *ctx, int offset, struct dhcp_option_82 option; option.t = DHO_DHCP_AGENT_OPTIONS; - option.len = sizeof(struct sub_option) + sizeof(struct sub_option); + option.len = sizeof (struct sub_option) + sizeof (struct sub_option); option.circuit_id.option_id = RAI_CIRCUIT_ID; - option.circuit_id.len = IF_NAMESIZE; - memcpy(option.circuit_id.val, dev, IF_NAMESIZE); - //option.circuit_id.val = bpf_ntohs(vlans->id[0]); + option.circuit_id.len = sizeof(option.circuit_id.val); + + /* Reconstruct VLAN device name + * Convert VLAN tags to ASCII from right to left, starting with + * inner VLAN tag. + * Device name is 16 characters long and prepended with dash, e.g.: + * ----ens6f0.83.20 + * We avoid null bytes to ensure compatibility with DHCP servers that + * interpret null as a string terminator. + */ + + char buf[IF_NAMESIZE]; + memset(buf, '-', sizeof (buf)); + + int c = VLAN_ASCII_MAX; /* We will need 4 bytes at most */ + int i = IF_NAMESIZE - 1; + __u16 inner_vlan = vlans->id[1]; + __u16 outer_vlan = vlans->id[0]; + + for (c = VLAN_ASCII_MAX; c > 0; c--) { + buf[i--] = (inner_vlan % 10) + '0'; + inner_vlan /= 10; + if (inner_vlan == 0) { + break; + } + } + + buf[i--] = '.'; + + for (c = VLAN_ASCII_MAX; c > 0; c--) { + buf[i--] = (outer_vlan % 10) + '0'; + outer_vlan /= 10; + if (outer_vlan == 0) { + break; + } + } + + + buf[i--] = '.'; + + for (c = IF_NAMESIZE - 1; c >= 0; c--) { + + if (dev[c] != 0) { + buf[i--] = dev[c]; + } + + if (i < 0) { + break; + } + + } + + if(sizeof(option.circuit_id.val) == sizeof(buf)) { + memcpy(option.circuit_id.val, buf, sizeof(buf)); + } + + /* Initialize remote ID */ + memset(option.remote_id.val, 0, sizeof(option.remote_id.val)); option.remote_id.option_id = RAI_REMOTE_ID; - option.remote_id.len = IF_NAMESIZE; - //option.remote_id.val = bpf_ntohs(vlans->id[1]); + option.remote_id.len = sizeof(option.remote_id.val); return xdp_store_bytes(ctx, offset, &option, sizeof (option), 0); } @@ -131,7 +185,7 @@ 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("Tail extended packet by %i bytes", delta); void *data_end = (void *) (long) ctx->data_end; @@ -207,10 +261,10 @@ int xdp_dhcp_relay(struct xdp_md *ctx) { /* 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); @@ -228,7 +282,7 @@ 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); @@ -282,13 +336,13 @@ int xdp_dhcp_relay(struct xdp_md *ctx) { bpf_printk("Broadcast packet received, opcode %i, hops %i", dhcp->op, dhcp->hops); - // Set destination MAC + /* Set destination MAC */ memcpy(eth->h_dest, relay_hwaddr, ETH_ALEN); // Set source MAC //memcpy(eth->h_source, relay_hwaddr, ETH_ALEN); - // Set GIADDR + /* Set GIADDR */ if (&dhcp->giaddr.s_addr + sizeof (relay_agent_ip) > data_end) { rc = XDP_ABORTED; goto out; @@ -386,7 +440,7 @@ 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; diff --git a/dhcp-relay/dhcp_user_xdp.c b/dhcp-relay/dhcp_user_xdp.c index efe2a21..2aeb56e 100644 --- a/dhcp-relay/dhcp_user_xdp.c +++ b/dhcp-relay/dhcp_user_xdp.c @@ -95,6 +95,57 @@ int xdp_link_attach(int ifindex, __u32 xdp_flags, int prog_fd) { */ int main(int argc, char **argv) { + /*char device[500] = "ens6f0np0"; + char o82[30] = { 0 }; + + int outer_vlan = 80; + int inner_vlan = 25; + + char str[30] = {0}; // large enough for an int even on 64-bit + int i = 30; + int c = 0; + + for(c = 4; c > 0; c--) { + str[i--] = (inner_vlan % 10) + '0'; + inner_vlan /= 10; + if(inner_vlan == 0) { + break; + } + } + + str[i--] = '.'; + + for(c = 4; c > 0; c--) { + str[i--] = (outer_vlan % 10) + '0'; + outer_vlan /= 10; + if(outer_vlan == 0) { + break; + } + } + + str[i--] = '.'; + + int y; + for(y = sizeof(device) - 1; y >= 0; y--) { + if(device[y] != 0) { + str[i] = device[y]; + i--; + } + } + + printf("i is %i\n", i); + + memset(o82, 0, 30); + memcpy(o82, str + i + 1, 30 - i); + + printf("The number was: %s\n", str + i + 1); + + printf("Option 82: %s\n", o82); + + printf("Option 82 length was %i\n", 30 - i); + + return 0;*/ + char filename[256] = "dhcp_kern_xdp.o"; int prog_fd, err; int opt; @@ -198,10 +249,10 @@ int main(int argc, char **argv) { __u64 hwaddr = 0; memcpy(&hwaddr, (unsigned char *) ifr.ifr_hwaddr.sa_data, 6); - + //display mac address 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); if (err) { @@ -248,7 +299,7 @@ int main(int argc, char **argv) { XDP_OBJ); exit(-1); } - + // Set relay agent MAC address key = 2; err = bpf_map_update_elem(map_fd, &key, &hwaddr, BPF_ANY); @@ -258,7 +309,7 @@ int main(int argc, char **argv) { exit(-1); } - + /* read the map from prog object file and update the real * server IP to the map */ @@ -284,7 +335,7 @@ int main(int argc, char **argv) { XDP_OBJ); exit(-1); } - + err = xdp_link_attach(ifindex, xdp_flags, prog_fd); if (err) return err;