mirror of
https://github.com/xdp-project/BNG-router.git
synced 2024-05-06 15:54:53 +00:00
Moving Option 82 to end of options as per RFC3046 section 2.1.
Adjusting tail instead of headers because of complications with variable length DHCP options - beware that this doesn't appear to work with veth interfaces. Use a physical NIC for testing for now.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
Usage
|
||||
-----
|
||||
dhcp_user_xdp takes network interface and dhcp relay server IP
|
||||
as inputs and stores it in a map. Filters the incoming DHCP requests and inserts
|
||||
dhcp_user_xdp takes network interface, DHCP server IP and DHCP relay agent IP
|
||||
as inputs and stores them in a map. Filters the incoming DHCP requests and inserts
|
||||
option 82 in the DHCP request packets and overwrites the destination IP to that
|
||||
of DHCP relay server IP.
|
||||
|
||||
@@ -10,7 +10,7 @@ cd bpf-examples/dhcp-relay
|
||||
make
|
||||
|
||||
Loading bpf program:
|
||||
sudo ./dhcp_user_xdp -i <netif> -d <dhcp relay IP>
|
||||
sudo ./dhcp_user_xdp -i <netif> -d <dhcp server IP> -s <dhcp relay agent IP>
|
||||
where,
|
||||
netif: Ingress network interface name
|
||||
|
||||
@@ -20,6 +20,12 @@ sudo ./dhcp_user_xdp -i <netif> -u
|
||||
To run in SKB mode:
|
||||
add option "-m skb" for both load and uload commands
|
||||
|
||||
Verify using tcpdump:
|
||||
sudo tcpdump -s 0 -i <netif> port 67 and port 68 -vvv
|
||||
Please beware that testing requires a physical NIC because we tail extend
|
||||
packets due to complications with variable length DHCP options.
|
||||
|
||||
Enable forwarding and allow local address spoofing:
|
||||
sudo echo 1 > /proc/sys/net/ipv4/ip_forward
|
||||
sudo echo 1 > /proc/sys/net/ipv4/conf/all/accept_local
|
||||
|
||||
Verify using tcpdump:
|
||||
sudo tcpdump -s 0 -i <netif> port 67 and port 68 -vvv
|
@@ -12,10 +12,12 @@
|
||||
#define RAI_REMOTE_ID 2
|
||||
#define RAI_OPTION_LEN 2
|
||||
|
||||
#define DEST_PORT 67 /* UDP destination port for dhcp */
|
||||
#define MAX_BYTES 280 /* Max bytes supported by xdp load/store apis */
|
||||
#define DHCP_SERVER_PORT 67
|
||||
#define DHCP_CLIENT_PORT 68
|
||||
#define DHCP_REQUEST 1
|
||||
#define DHCP_REPLY 2
|
||||
|
||||
/* structure for sub-options in option 82*/
|
||||
/* Structure for sub-options in option 82 */
|
||||
struct sub_option {
|
||||
__u8 option_id;
|
||||
__u8 len;
|
||||
@@ -30,6 +32,11 @@ struct dhcp_option_82 {
|
||||
struct sub_option remote_id;
|
||||
};
|
||||
|
||||
/*structure for dhcp option 255 */
|
||||
struct dhcp_option_255 {
|
||||
__u8 t;
|
||||
};
|
||||
|
||||
struct dhcp_packet {
|
||||
__u8 op; /* 0: Message opcode/type */
|
||||
__u8 htype; /* 1: Hardware addr type (net/if_types.h) */
|
||||
|
@@ -8,49 +8,66 @@
|
||||
#include "dhcp-relay.h"
|
||||
|
||||
/*
|
||||
* This map is for storing the DHCP relay server
|
||||
* IP address configured by user. It is received
|
||||
* as an argument by user program.
|
||||
*/
|
||||
* This map is for storing the DHCP relay configuration, including:
|
||||
*
|
||||
* Relay server IP address
|
||||
* Relay agent IP address
|
||||
* Relay agent MAC address
|
||||
*
|
||||
* Configuration parameters are set by CLI arguments in user space program.
|
||||
*/
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_ARRAY);
|
||||
__type(key, __u32);
|
||||
__type(value, __u32);
|
||||
__uint(max_entries, 1);
|
||||
} dhcp_server SEC(".maps");
|
||||
__type(value, __u64);
|
||||
__uint(max_entries, 3);
|
||||
} relay_config SEC(".maps");
|
||||
|
||||
/* Inserts DHCP option 82 into the received dhcp packet
|
||||
* at the specified offset.
|
||||
*/
|
||||
static __always_inline int write_dhcp_option(void *ctx, int offset,
|
||||
struct collect_vlans *vlans)
|
||||
{
|
||||
*/
|
||||
static __always_inline int write_dhcp_option_82(void *ctx, int offset,
|
||||
struct collect_vlans *vlans) {
|
||||
struct dhcp_option_82 option;
|
||||
|
||||
option.t = DHO_DHCP_AGENT_OPTIONS;
|
||||
option.len = 8;
|
||||
option.circuit_id.option_id = RAI_CIRCUIT_ID;
|
||||
option.circuit_id.len = RAI_OPTION_LEN;
|
||||
option.circuit_id.val = bpf_htons(vlans->id[0]);
|
||||
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_htons(vlans->id[1]);
|
||||
option.remote_id.val = bpf_ntohs(vlans->id[1]);
|
||||
|
||||
return xdp_store_bytes(ctx, offset, &option, sizeof(option), 0);
|
||||
return xdp_store_bytes(ctx, offset, &option, sizeof (option), 0);
|
||||
}
|
||||
|
||||
/* Inserts DHCP option 255 into the received dhcp packet
|
||||
* at the specified offset.
|
||||
*/
|
||||
static __always_inline int write_dhcp_option_255(void *ctx, int offset) {
|
||||
struct dhcp_option_255 option;
|
||||
|
||||
option.t = 255;
|
||||
|
||||
return xdp_store_bytes(ctx, offset, &option, sizeof (option), 0);
|
||||
}
|
||||
|
||||
/* Calculates the IP checksum */
|
||||
static __always_inline int calc_ip_csum(struct iphdr *oldip, struct iphdr *ip,
|
||||
__u32 oldcsum)
|
||||
{
|
||||
__u32 size = sizeof(struct iphdr);
|
||||
__u32 csum = bpf_csum_diff((__be32 *)oldip, size, (__be32 *)ip, size,
|
||||
~oldcsum);
|
||||
__u32 oldcsum) {
|
||||
__u32 size = sizeof (struct iphdr);
|
||||
__u32 csum = bpf_csum_diff((__be32 *) oldip, size, (__be32 *) ip, size,
|
||||
~oldcsum);
|
||||
__u32 sum = (csum >> 16) + (csum & 0xffff);
|
||||
sum += (sum >> 16);
|
||||
return sum;
|
||||
}
|
||||
|
||||
#define dhcp_offset \
|
||||
sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr)
|
||||
//offsetof(struct dhcp_packet, options)
|
||||
|
||||
/* Offset to DHCP Options part of the packet */
|
||||
#define static_offset \
|
||||
sizeof(struct ethhdr) + sizeof(struct iphdr) + sizeof(struct udphdr) + \
|
||||
@@ -59,26 +76,56 @@ static __always_inline int calc_ip_csum(struct iphdr *oldip, struct iphdr *ip,
|
||||
/* Delta value to be adjusted at xdp head*/
|
||||
#define delta sizeof(struct dhcp_option_82)
|
||||
|
||||
#ifndef DHCP_MAX_OPTIONS
|
||||
#define DHCP_MAX_OPTIONS 20
|
||||
#endif
|
||||
|
||||
/* buf needs to be a static global var because the verifier won't allow
|
||||
* unaligned stack accesses
|
||||
*/
|
||||
static __u8 buf[static_offset + VLAN_MAX_DEPTH * sizeof(struct vlan_hdr)];
|
||||
*/
|
||||
//static __u8 buf[static_offset + VLAN_MAX_DEPTH * sizeof (struct vlan_hdr)];
|
||||
|
||||
#define bpf_printk(fmt, ...) \
|
||||
({ \
|
||||
char ____fmt[] = fmt; \
|
||||
bpf_trace_printk(____fmt, sizeof(____fmt), \
|
||||
##__VA_ARGS__); \
|
||||
})
|
||||
|
||||
/* XDP program for parsing the DHCP packet and inserting the option 82*/
|
||||
SEC(XDP_PROG_SEC)
|
||||
int xdp_dhcp_relay(struct xdp_md *ctx)
|
||||
{
|
||||
void *data_end = (void *)(long)ctx->data_end;
|
||||
void *data = (void *)(long)ctx->data;
|
||||
struct collect_vlans vlans = { 0 };
|
||||
int xdp_dhcp_relay(struct xdp_md *ctx) {
|
||||
|
||||
bpf_printk("\n");
|
||||
|
||||
/* Tail extend packet */
|
||||
int res = bpf_xdp_adjust_tail(ctx, delta);
|
||||
if (res != 0) {
|
||||
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);
|
||||
|
||||
void *data_end = (void *) (long) ctx->data_end;
|
||||
void *data = (void *) (long) ctx->data;
|
||||
struct collect_vlans vlans = {0};
|
||||
struct ethhdr *eth;
|
||||
struct iphdr *ip;
|
||||
struct iphdr oldip;
|
||||
struct udphdr *udp;
|
||||
__u32 *dhcp_srv;
|
||||
struct dhcp_packet *dhcp;
|
||||
__u32 *dhcp_srv_ip;
|
||||
__u32 *relay_agent_ip;
|
||||
__u64 *relay_hwaddr;
|
||||
int rc = XDP_PASS;
|
||||
__u16 offset = static_offset;
|
||||
__u16 option_offset = offset;
|
||||
__u16 ip_offset = 0;
|
||||
__u16 vlan_length = 0;
|
||||
__u8 option_code = 0;
|
||||
__u8 option_length = 0;
|
||||
int i = 0;
|
||||
|
||||
/* These keep track of the next header type and iterator pointer */
|
||||
@@ -95,89 +142,254 @@ int xdp_dhcp_relay(struct xdp_md *ctx)
|
||||
ether_type = parse_ethhdr_vlan(&nh, data_end, ð, &vlans);
|
||||
/* check for valid ether type */
|
||||
if (ether_type < 0) {
|
||||
bpf_printk("Cannot determine ethertype");
|
||||
rc = XDP_ABORTED;
|
||||
goto out;
|
||||
}
|
||||
if (ether_type != bpf_htons(ETH_P_IP))
|
||||
if (ether_type != bpf_htons(ETH_P_IP)) {
|
||||
bpf_printk("Ethertype %#x is not ETH_P_IP", bpf_ntohs(ether_type));
|
||||
goto out;
|
||||
}
|
||||
|
||||
bpf_printk("Ethertype %x", bpf_ntohs(ether_type));
|
||||
|
||||
/* Check at least two vlan tags are present */
|
||||
if (vlans.id[1] == 0)
|
||||
goto out;
|
||||
|
||||
/* Read dhcp relay server IP from map */
|
||||
dhcp_srv = bpf_map_lookup_elem(&dhcp_server, &key);
|
||||
if (dhcp_srv == NULL)
|
||||
if (vlans.id[1] == 0) {
|
||||
bpf_printk("No VLAN tags set");
|
||||
goto out;
|
||||
}
|
||||
|
||||
h_proto = parse_iphdr(&nh, data_end, &ip);
|
||||
|
||||
/* only handle fixed-size IP header due to static copy */
|
||||
/* Only handle fixed-size IP header due to static copy */
|
||||
if (h_proto != IPPROTO_UDP || ip->ihl > 5) {
|
||||
goto out;
|
||||
}
|
||||
/*old ip hdr backup for re-calculating the checksum later*/
|
||||
|
||||
/* Old ip hdr backup for re-calculating the checksum later */
|
||||
oldip = *ip;
|
||||
ip_offset = ((void *)ip - data) & 0x3fff;
|
||||
ip_offset = ((void *) ip - data) & 0x3fff;
|
||||
len = parse_udphdr(&nh, data_end, &udp);
|
||||
if (len < 0)
|
||||
goto out;
|
||||
|
||||
if (udp->dest != bpf_htons(DEST_PORT))
|
||||
/* Handle DHCP packets only */
|
||||
if (udp->dest != bpf_htons(DHCP_SERVER_PORT) && udp->dest != bpf_htons(DHCP_CLIENT_PORT))
|
||||
goto out;
|
||||
|
||||
if (xdp_load_bytes(ctx, 0, buf, static_offset))
|
||||
/* Read DHCP server IP from config map */
|
||||
key = 0;
|
||||
dhcp_srv_ip = bpf_map_lookup_elem(&relay_config, &key);
|
||||
if (dhcp_srv_ip == NULL)
|
||||
goto out;
|
||||
|
||||
/* Read relay agent IP from config map */
|
||||
key = 1;
|
||||
relay_agent_ip = bpf_map_lookup_elem(&relay_config, &key);
|
||||
if (relay_agent_ip == NULL)
|
||||
goto out;
|
||||
|
||||
/* Read relay agent MAC address from config map */
|
||||
key = 2;
|
||||
relay_hwaddr = bpf_map_lookup_elem(&relay_config, &key);
|
||||
if (relay_hwaddr == NULL)
|
||||
goto out;
|
||||
|
||||
/* Copy headers of packet to buf */
|
||||
//if (xdp_load_bytes(ctx, 0, buf, static_offset))
|
||||
// goto out;
|
||||
|
||||
/* Increment offset by 4 bytes for each VLAN (to accomodate VLAN headers */
|
||||
for (i = 0; i < VLAN_MAX_DEPTH; i++) {
|
||||
if (vlans.id[i]) {
|
||||
if (xdp_load_bytes(ctx, offset, buf + offset, 4))
|
||||
goto out;
|
||||
|
||||
bpf_printk("Found VLAN tag %i at depth %i", vlans.id[i], i);
|
||||
|
||||
/* For each VLAN present, copy 4 bytes of DHCP options to buffer */
|
||||
//if (xdp_load_bytes(ctx, offset, buf + offset, 4))
|
||||
// goto out;
|
||||
offset += 4;
|
||||
vlan_length += 4;
|
||||
}
|
||||
}
|
||||
|
||||
/* adjusting the packet head by delta size to insert option82 */
|
||||
if (bpf_xdp_adjust_head(ctx, 0 - (int)delta) < 0)
|
||||
return XDP_ABORTED;
|
||||
/* Find packet boundaries */
|
||||
data_end = (void *) (long) ctx->data_end;
|
||||
data = (void *) (long) ctx->data;
|
||||
|
||||
data_end = (void *)(long)ctx->data_end;
|
||||
data = (void *)(long)ctx->data;
|
||||
/* Parse DHCP packet */
|
||||
if (data + vlan_length + dhcp_offset + sizeof (dhcp) > data_end) {
|
||||
goto out;
|
||||
}
|
||||
dhcp = data + vlan_length + dhcp_offset;
|
||||
|
||||
bpf_printk("Parsing DHCP packet, opcode %i, hops %i", dhcp->op, dhcp->hops);
|
||||
|
||||
if (dhcp->op == DHCP_REQUEST && (eth->h_dest[0] == 0xff
|
||||
&& eth->h_dest[1] == 0xff
|
||||
&& eth->h_dest[2] == 0xff
|
||||
&& eth->h_dest[3] == 0xff
|
||||
&& eth->h_dest[4] == 0xff
|
||||
&& eth->h_dest[5] == 0xff)) {
|
||||
|
||||
/* Request from client received as broadcast */
|
||||
|
||||
bpf_printk("Broadcast packet received, opcode %i, hops %i", dhcp->op, dhcp->hops);
|
||||
|
||||
// Set destination MAC
|
||||
memcpy(eth->h_dest, relay_hwaddr, ETH_ALEN);
|
||||
|
||||
// Set source MAC
|
||||
//memcpy(eth->h_source, relay_hwaddr, ETH_ALEN);
|
||||
|
||||
// Set GIADDR
|
||||
if(&dhcp->giaddr.s_addr + sizeof(relay_agent_ip) > data_end) {
|
||||
rc = XDP_ABORTED;
|
||||
goto out;
|
||||
}
|
||||
dhcp->giaddr.s_addr = *relay_agent_ip;
|
||||
|
||||
|
||||
} else if (dhcp->op == DHCP_REPLY && (eth->h_dest[0] != 0xff
|
||||
|| eth->h_dest[1] != 0xff
|
||||
|| eth->h_dest[2] != 0xff
|
||||
|| eth->h_dest[3] != 0xff
|
||||
|| eth->h_dest[4] != 0xff
|
||||
|| eth->h_dest[5] != 0xff)) {
|
||||
|
||||
/* Response from server received as unicast */
|
||||
|
||||
bpf_printk("Unicast packet received, opcode %i, hops %i", dhcp->op, dhcp->hops);
|
||||
|
||||
/* FIXME: Add code for reply packets
|
||||
* Basically:
|
||||
* - Set dest and src MAC
|
||||
* - Add VLAN tags
|
||||
* - Remove option 82
|
||||
* - Use XDP_TX (or XDP_REDIRECT) to send the response
|
||||
* to the end user
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
/* Check hops */
|
||||
if (dhcp->hops > 16) {
|
||||
bpf_printk("Max hops exceeded, discarding packet");
|
||||
rc = XDP_ABORTED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Increment hops */
|
||||
dhcp->hops++;
|
||||
|
||||
/* Check if we exceed boundaries to make verifier happy */
|
||||
if (data + offset > data_end)
|
||||
return XDP_ABORTED;
|
||||
goto out;
|
||||
|
||||
if (xdp_store_bytes(ctx, 0, buf, static_offset, 0))
|
||||
return XDP_ABORTED;
|
||||
option_offset = offset;
|
||||
|
||||
if (offset > static_offset) {
|
||||
__u8 *pos = (__u8 *) (data + option_offset);
|
||||
|
||||
/* Loop through all DHCP options */
|
||||
#pragma unroll DHCP_MAX_OPTIONS
|
||||
for (i = 0; i < DHCP_MAX_OPTIONS; i++) {
|
||||
|
||||
/* Verifier check */
|
||||
if (pos + 1 > data_end)
|
||||
break;
|
||||
|
||||
option_code = *pos;
|
||||
|
||||
bpf_printk("Got option code %i at offset %i, hex %x", option_code, option_offset, option_offset);
|
||||
|
||||
if (option_code == 255) {
|
||||
|
||||
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)) {
|
||||
bpf_printk("Could not write DHCP option 82 at offset %i", option_offset);
|
||||
return XDP_ABORTED;
|
||||
}
|
||||
|
||||
/* Set END option */
|
||||
|
||||
/* Verifier check */
|
||||
if (pos + delta > data_end) {
|
||||
return XDP_ABORTED;
|
||||
}
|
||||
pos += delta;
|
||||
option_offset += delta;
|
||||
|
||||
if (write_dhcp_option_255(ctx, option_offset)) {
|
||||
bpf_printk("Could not write DHCP option 255 at offset %i", option_offset);
|
||||
return XDP_ABORTED;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
pos++;
|
||||
|
||||
option_length = *pos;
|
||||
option_offset += option_length + 2;
|
||||
|
||||
if (pos + 1 > data_end) {
|
||||
break;
|
||||
}
|
||||
pos++;
|
||||
|
||||
if (pos + option_length > data_end) {
|
||||
break;
|
||||
}
|
||||
pos += option_length;
|
||||
|
||||
}
|
||||
|
||||
//return XDP_PASS;
|
||||
|
||||
/* Copy stored headers from buf to context */
|
||||
/*if (xdp_store_bytes(ctx, 0, buf, static_offset, 0)) {
|
||||
|
||||
bpf_printk("xdp_store_bytes(ctx, 0, buf, %i) failed", static_offset);
|
||||
return XDP_ABORTED;
|
||||
}*/
|
||||
|
||||
|
||||
/* make space for option 82 - copy DHCP options after increasing offset */
|
||||
/*if (offset > static_offset) {
|
||||
offset = static_offset;
|
||||
for (i = 0; i < VLAN_MAX_DEPTH; i++) {
|
||||
if (vlans.id[i]) {
|
||||
if (xdp_store_bytes(ctx, offset, buf + offset,
|
||||
4, 0))
|
||||
return XDP_ABORTED;
|
||||
offset += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (write_dhcp_option(ctx, offset, &vlans))
|
||||
if (vlans.id[i]) {*/
|
||||
/* */
|
||||
/*if (xdp_store_bytes(ctx, offset, buf + offset,
|
||||
4, 0))
|
||||
return XDP_ABORTED;
|
||||
offset += 4;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
ip = data + ip_offset;
|
||||
if (ip + 1 > data_end)
|
||||
return XDP_ABORTED;
|
||||
|
||||
/* overwrite the destination IP in IP header */
|
||||
ip->daddr = *dhcp_srv;
|
||||
/* Overwrite the destination IP in IP header */
|
||||
ip->daddr = *dhcp_srv_ip;
|
||||
|
||||
//re-calc ip checksum
|
||||
/* Overwrite source IP */
|
||||
ip->saddr = *relay_agent_ip;
|
||||
|
||||
/* Re-calculate ip checksum */
|
||||
__u32 sum = calc_ip_csum(&oldip, ip, oldip.check);
|
||||
ip->check = ~sum;
|
||||
rc = XDP_PASS;
|
||||
|
||||
bpf_printk("Wrote DHCP option at offset %i, returning XDP_PASS", offset);
|
||||
|
||||
goto out;
|
||||
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
@@ -13,21 +13,24 @@
|
||||
#include <linux/if_link.h> /* depend on kernel-headers installed */
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#define SERVER_MAP "dhcp_server"
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define SERVER_MAP "relay_config"
|
||||
#define XDP_OBJ "dhcp_kern_xdp.o"
|
||||
|
||||
static const struct option options[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "help", no_argument, NULL, 'h'},
|
||||
{ "interface", required_argument, NULL,
|
||||
'i' }, // Name of interface to run on
|
||||
{ "dhcp-server", required_argument, NULL, 'd' },
|
||||
{ "mode", required_argument, NULL, 'm' },
|
||||
{ "unload", no_argument, NULL, 'u' },
|
||||
{ 0, 0, NULL, 0 }
|
||||
'i'}, // Name of interface to run on
|
||||
{ "dhcp-server", required_argument, NULL, 'd'},
|
||||
{ "relay-agent-address", required_argument, NULL, 's'},
|
||||
{ "mode", required_argument, NULL, 'm'},
|
||||
{ "unload", no_argument, NULL, 'u'},
|
||||
{ 0, 0, NULL, 0}
|
||||
};
|
||||
|
||||
static void print_usage(char *argv[])
|
||||
{
|
||||
static void print_usage(char *argv[]) {
|
||||
int i;
|
||||
printf("Usage:\n");
|
||||
printf("%s\n", argv[0]);
|
||||
@@ -45,8 +48,7 @@ static void print_usage(char *argv[])
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static int xdp_link_detach(int ifindex, __u32 xdp_flags)
|
||||
{
|
||||
static int xdp_link_detach(int ifindex, __u32 xdp_flags) {
|
||||
int err;
|
||||
|
||||
if ((err = bpf_set_link_xdp_fd(ifindex, -1, xdp_flags)) < 0) {
|
||||
@@ -57,8 +59,7 @@ static int xdp_link_detach(int ifindex, __u32 xdp_flags)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xdp_link_attach(int ifindex, __u32 xdp_flags, int prog_fd)
|
||||
{
|
||||
int xdp_link_attach(int ifindex, __u32 xdp_flags, int prog_fd) {
|
||||
int err;
|
||||
|
||||
/* libbpf provide the XDP net_device link-level hook attach helper */
|
||||
@@ -71,15 +72,15 @@ int xdp_link_attach(int ifindex, __u32 xdp_flags, int prog_fd)
|
||||
ifindex, -err, strerror(-err));
|
||||
|
||||
switch (-err) {
|
||||
case EBUSY:
|
||||
case EEXIST:
|
||||
fprintf(stderr, "Hint: XDP already loaded on device\n");
|
||||
break;
|
||||
case EOPNOTSUPP:
|
||||
fprintf(stderr, "Hint: Native-XDP not supported\n");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
case EBUSY:
|
||||
case EEXIST:
|
||||
fprintf(stderr, "Hint: XDP already loaded on device\n");
|
||||
break;
|
||||
case EOPNOTSUPP:
|
||||
fprintf(stderr, "Hint: Native-XDP not supported\n");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@@ -90,9 +91,9 @@ int xdp_link_attach(int ifindex, __u32 xdp_flags, int prog_fd)
|
||||
/* User program takes two or three arguments
|
||||
* interface name, relay server IP and prog
|
||||
* unload flag
|
||||
*/
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
*/
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
char filename[256] = "dhcp_kern_xdp.o";
|
||||
int prog_fd, err;
|
||||
int opt;
|
||||
@@ -104,64 +105,100 @@ int main(int argc, char **argv)
|
||||
struct bpf_object *obj = NULL;
|
||||
int map_fd;
|
||||
int key = 0;
|
||||
struct in_addr addr = {};
|
||||
bool addr_set = false;
|
||||
struct in_addr dhcp_server_addr = {};
|
||||
struct in_addr relay_agent_addr = {};
|
||||
bool dhcp_server_addr_set = false;
|
||||
bool relay_agent_addr_set = false;
|
||||
__u16 ifindex = 0;
|
||||
int fd;
|
||||
unsigned char *mac;
|
||||
struct ifreq ifr;
|
||||
|
||||
while ((opt = getopt_long(argc, argv, "hui:d:m:", options, NULL)) !=
|
||||
-1) {
|
||||
while ((opt = getopt_long(argc, argv, "hui:d:m:s:", options, NULL)) !=
|
||||
-1) {
|
||||
switch (opt) {
|
||||
case 'i':
|
||||
strncpy(dev, optarg, IF_NAMESIZE);
|
||||
dev[IF_NAMESIZE - 1] = '\0';
|
||||
ifindex = if_nametoindex(dev);
|
||||
if (ifindex <= 0) {
|
||||
printf("Couldn't find ifname:%s \n", dev);
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case 'd':
|
||||
if (inet_aton(optarg, &addr) == 0) {
|
||||
fprintf(stderr,
|
||||
"Couldn't validate IP address:%s\n",
|
||||
optarg);
|
||||
return -EINVAL;
|
||||
}
|
||||
addr_set = true;
|
||||
break;
|
||||
case 'm':
|
||||
if (strcmp(optarg, "skb") == 0) {
|
||||
xdp_flags = XDP_FLAGS_SKB_MODE;
|
||||
} else if (strcmp(optarg, "drv") != 0) {
|
||||
fprintf(stderr, "Invalid mode: %s\n", optarg);
|
||||
return -EINVAL;
|
||||
}
|
||||
case 'i':
|
||||
strncpy(dev, optarg, IF_NAMESIZE);
|
||||
dev[IF_NAMESIZE - 1] = '\0';
|
||||
ifindex = if_nametoindex(dev);
|
||||
if (ifindex <= 0) {
|
||||
printf("Couldn't find ifname:%s \n", dev);
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
case 'd': // DHCP server address
|
||||
if (inet_aton(optarg, &dhcp_server_addr) == 0) {
|
||||
fprintf(stderr,
|
||||
"Couldn't validate DHCP server IP address:%s\n",
|
||||
optarg);
|
||||
return -EINVAL;
|
||||
}
|
||||
dhcp_server_addr_set = true;
|
||||
break;
|
||||
case 's': // Relay agent address
|
||||
if (inet_aton(optarg, &relay_agent_addr) == 0) {
|
||||
fprintf(stderr,
|
||||
"Couldn't validate relay agent IP address:%s\n",
|
||||
optarg);
|
||||
return -EINVAL;
|
||||
}
|
||||
relay_agent_addr_set = true;
|
||||
break;
|
||||
case 'm':
|
||||
if (strcmp(optarg, "skb") == 0) {
|
||||
xdp_flags = XDP_FLAGS_SKB_MODE;
|
||||
} else if (strcmp(optarg, "drv") != 0) {
|
||||
fprintf(stderr, "Invalid mode: %s\n", optarg);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
break;
|
||||
case 'u':
|
||||
do_unload = 1;
|
||||
break;
|
||||
case 'h':
|
||||
print_usage(argv);
|
||||
exit(0);
|
||||
default:
|
||||
fprintf(stderr, "Unknown option %s\n", argv[optind]);
|
||||
return -EINVAL;
|
||||
break;
|
||||
case 'u':
|
||||
do_unload = 1;
|
||||
break;
|
||||
case 'h':
|
||||
print_usage(argv);
|
||||
exit(0);
|
||||
default:
|
||||
fprintf(stderr, "Unknown option %s\n", argv[optind]);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ifindex) {
|
||||
fprintf(stderr, "Missing ifname\n");
|
||||
fprintf(stderr, "Please specify interface name\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!addr_set) {
|
||||
fprintf(stderr, "Missing server address\n");
|
||||
if (!dhcp_server_addr_set) {
|
||||
fprintf(stderr, "Please specify DHCP server address with -d parameter\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!relay_agent_addr_set) {
|
||||
fprintf(stderr, "Please specify DHCP relay agent address with -s parameter\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (do_unload)
|
||||
return xdp_link_detach(ifindex, xdp_flags);
|
||||
|
||||
// Find MAC address of interface
|
||||
fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
|
||||
ifr.ifr_addr.sa_family = AF_INET;
|
||||
strncpy(ifr.ifr_name, dev, IF_NAMESIZE);
|
||||
|
||||
ioctl(fd, SIOCGIFHWADDR, &ifr);
|
||||
|
||||
close(fd);
|
||||
|
||||
mac = (unsigned char *) ifr.ifr_hwaddr.sa_data;
|
||||
|
||||
__u64 hwaddr = 0;
|
||||
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]);
|
||||
|
||||
/* 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) {
|
||||
@@ -191,7 +228,27 @@ int main(int argc, char **argv)
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
err = bpf_map_update_elem(map_fd, &key, &addr.s_addr, BPF_ANY);
|
||||
// Set DHCP server address
|
||||
key = 0;
|
||||
err = bpf_map_update_elem(map_fd, &key, &dhcp_server_addr.s_addr, BPF_ANY);
|
||||
if (err) {
|
||||
fprintf(stderr, "Could not update map %s in %s\n", SERVER_MAP,
|
||||
XDP_OBJ);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// Set relay agent IP address
|
||||
key = 1;
|
||||
err = bpf_map_update_elem(map_fd, &key, &relay_agent_addr.s_addr, BPF_ANY);
|
||||
if (err) {
|
||||
fprintf(stderr, "Could not update map %s in %s\n", SERVER_MAP,
|
||||
XDP_OBJ);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// Set relay agent MAC address
|
||||
key = 2;
|
||||
err = bpf_map_update_elem(map_fd, &key, &hwaddr, BPF_ANY);
|
||||
if (err) {
|
||||
fprintf(stderr, "Could not update map %s in %s\n", SERVER_MAP,
|
||||
XDP_OBJ);
|
||||
|
Reference in New Issue
Block a user