Skip to content

Commit

Permalink
Reformatting option 82 circuit ID to include interface name (including
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Yoel Caspersen committed Oct 18, 2021
1 parent ac9373c commit a9003fa
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 21 deletions.
3 changes: 2 additions & 1 deletion dhcp-relay/dhcp-relay.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
84 changes: 69 additions & 15 deletions dhcp-relay/dhcp_kern_xdp.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

#include <linux/bpf.h>
#include <linux/in.h>
#include <net/if.h> /* IF_NAMESIZE */
#include <net/if.h> /* IF_NAMESIZE */
#include <bpf/bpf_helpers.h>
#include <xdp/parsing_helpers.h>
#include <xdp/context_helpers.h>
Expand Down Expand Up @@ -49,22 +49,76 @@ 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,
struct collect_vlans *vlans, char *dev) {
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);
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
61 changes: 56 additions & 5 deletions dhcp-relay/dhcp_user_xdp.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
Expand All @@ -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
*/
Expand All @@ -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;
Expand Down

0 comments on commit a9003fa

Please sign in to comment.