From b7307744e08bc601f1ae83773ee1afaa8b170f57 Mon Sep 17 00:00:00 2001 From: Franco Fichtner Date: Mon, 11 Jun 2018 17:57:05 +0200 Subject: [PATCH] dhclient: VLAN priority support These changes are based on the dhclient used in pfSense 2.3, which seems not to have made it into FreeBSD 11 as used in pfSense 2.4. To be able to add a VLAN priority to a DHCP request the following must be added to the config: interface "em1_vlan123" { vlan-parent "em1"; vlan-id 123; vlan-pcp 6; } Extensive (and annoying) care has been taken WRT keeping the BPF write filter functional for security reasons. Based on a submission by Martin Wasley . --- sbin/dhclient/bpf.c | 36 ++++++++++++++++++++++++++--------- sbin/dhclient/clparse.c | 15 +++++++++++++++ sbin/dhclient/conflex.c | 6 ++++++ sbin/dhclient/dhclient.conf.5 | 17 +++++++++++++++++ sbin/dhclient/dhcpd.h | 3 +++ sbin/dhclient/dhctoken.h | 3 +++ sbin/dhclient/dispatch.c | 16 +++++++++++++++- sbin/dhclient/packet.c | 28 ++++++++++++++++++--------- 8 files changed, 105 insertions(+), 19 deletions(-) diff --git a/sbin/dhclient/bpf.c b/sbin/dhclient/bpf.c index 78b9eb624e8..ab31cf8586c 100644 --- a/sbin/dhclient/bpf.c +++ b/sbin/dhclient/bpf.c @@ -3,6 +3,7 @@ /* BPF socket interface code, originally contributed by Archie Cobbs. */ /* + * Copyright (c) 2018 Franco Fichtner * Copyright (c) 1995, 1996, 1998, 1999 * The Internet Software Consortium. All rights reserved. * @@ -94,23 +95,40 @@ if_register_bpf(struct interface_info *info, int flags) * 'ip and udp and src port bootps and dst port (bootps or bootpc)' */ struct bpf_insn dhcp_bpf_wfilter[] = { + /* Set packet index for IP packet... */ + BPF_STMT(BPF_LDX + BPF_W + BPF_IMM, 0), + + /* Test whether this is a VLAN packet... */ + BPF_STMT(BPF_LD + BPF_H + BPF_IND, 12), + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_VLAN, 0, 1), + + /* Correct the packet index for VLAN... */ + BPF_STMT(BPF_LDX + BPF_W + BPF_IMM, 4), + + /* Make sure it is an IPv4 packet... */ BPF_STMT(BPF_LD + BPF_B + BPF_IND, 14), - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (IPVERSION << 4) + 5, 0, 12), + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (IPVERSION << 4) + 5, 0, 18), /* Make sure this is an IP packet... */ - BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 12), - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 10), + BPF_STMT(BPF_LD + BPF_H + BPF_IND, 12), + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 0, 16), /* Make sure it's a UDP packet... */ - BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 23), - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 8), + BPF_STMT(BPF_LD + BPF_B + BPF_IND, 23), + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 14), /* Make sure this isn't a fragment... */ - BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20), - BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 6, 0), /* patched */ + BPF_STMT(BPF_LD + BPF_H + BPF_IND, 20), + BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 12, 0), /* patched */ /* Get the IP header length... */ + BPF_STMT(BPF_MISC + BPF_TXA, 0), + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 2), BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 14), + BPF_JUMP(BPF_JMP + BPF_JA, 1, 0, 0), + BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 18), + BPF_STMT(BPF_ALU + BPF_ADD + BPF_X, 0), + BPF_STMT(BPF_MISC + BPF_TAX, 0), /* Make sure it's from the right port... */ BPF_STMT(BPF_LD + BPF_H + BPF_IND, 14), @@ -152,8 +170,8 @@ if_register_send(struct interface_info *info) p.bf_len = dhcp_bpf_wfilter_len; p.bf_insns = dhcp_bpf_wfilter; - if (dhcp_bpf_wfilter[7].k == 0x1fff) - dhcp_bpf_wfilter[7].k = htons(IP_MF|IP_OFFMASK); + if (dhcp_bpf_wfilter[11].k == 0x1fff) + dhcp_bpf_wfilter[11].k = htons(IP_MF|IP_OFFMASK); if (ioctl(info->wfdesc, BIOCSETWF, &p) < 0) error("Can't install write filter program: %m"); diff --git a/sbin/dhclient/clparse.c b/sbin/dhclient/clparse.c index e735e330655..9cad723b44f 100644 --- a/sbin/dhclient/clparse.c +++ b/sbin/dhclient/clparse.c @@ -75,6 +75,9 @@ read_client_conf(void) memset(&top_level_config, 0, sizeof(top_level_config)); /* Set some defaults... */ + top_level_config.vlan_id = 0; + top_level_config.vlan_pcp = 0; + top_level_config.vlan_parent = NULL; top_level_config.timeout = 60; top_level_config.select_interval = 0; top_level_config.reboot_timeout = 10; @@ -198,6 +201,7 @@ parse_client_statement(FILE *cfile, struct interface_info *ip, int token; char *val; struct option *option; + time_t tmp; switch (next_token(&val, cfile)) { case SEND: @@ -257,6 +261,17 @@ parse_client_statement(FILE *cfile, struct interface_info *ip, case REBOOT: parse_lease_time(cfile, &config->reboot_timeout); return; + case VLAN_ID: + parse_lease_time(cfile, &tmp); + config->vlan_id = (int)tmp; + return; + case VLAN_PARENT: + config->vlan_parent = parse_string(cfile); + return; + case VLAN_PCP: + parse_lease_time(cfile, &tmp); + config->vlan_pcp = (int)tmp; + return; case BACKOFF_CUTOFF: parse_lease_time(cfile, &config->backoff_cutoff); return; diff --git a/sbin/dhclient/conflex.c b/sbin/dhclient/conflex.c index cd57e3b4413..7fc95d969cd 100644 --- a/sbin/dhclient/conflex.c +++ b/sbin/dhclient/conflex.c @@ -519,6 +519,12 @@ intern(char *atom, int dfv) case 'v': if (!strcasecmp(atom + 1, "endor-class")) return (VENDOR_CLASS); + if (!strcasecmp(atom + 1, "lan-id")) + return (VLAN_ID); + if (!strcasecmp(atom + 1, "lan-parent")) + return (VLAN_PARENT); + if (!strcasecmp(atom + 1, "lan-pcp")) + return (VLAN_PCP); break; case 'y': if (!strcasecmp(atom + 1, "iaddr")) diff --git a/sbin/dhclient/dhclient.conf.5 b/sbin/dhclient/dhclient.conf.5 index fb9d9f17048..9e3584c6344 100644 --- a/sbin/dhclient/dhclient.conf.5 +++ b/sbin/dhclient/dhclient.conf.5 @@ -437,6 +437,23 @@ script, and expiry times are ignored. A typical alias declaration includes an interface declaration, a fixed-address declaration for the IP alias address, and a subnet-mask option declaration. A medium statement should never be included in an alias declaration. +.Sh VLAN DECLARATIONS +In order to be able to send a request using a specific VLAN PCP all of the +following options are required to emit a properly formed frame. +.Bl -tag -width indent +.It Ic vlan-parent Qo Ar interface Qc ; +The +.Ic vlan-parent +allows attaching to the parent interface of the VLAN. +.It Ic vlan-id Ar identifier ; +The +.Ic vlan-id +sets the desired indentifier of the VLAN. +.It Ic vlan-pcp Ar priority ; +The +.Ic vlan-pcp +sets the desired priority code point value of the VLAN. +.El .Sh OTHER DECLARATIONS .Bl -tag -width indent .It Ic reject Ar ip-address ; diff --git a/sbin/dhclient/dhcpd.h b/sbin/dhclient/dhcpd.h index 479753efb37..e63a79bfec9 100644 --- a/sbin/dhclient/dhcpd.h +++ b/sbin/dhclient/dhcpd.h @@ -154,6 +154,9 @@ struct client_config { u_int8_t required_options[256]; u_int8_t requested_options[256]; int requested_option_count; + int vlan_id; + int vlan_pcp; + char *vlan_parent; time_t timeout; time_t initial_interval; time_t retry_interval; diff --git a/sbin/dhclient/dhctoken.h b/sbin/dhclient/dhctoken.h index 7b23242fbac..7c65ff79a8e 100644 --- a/sbin/dhclient/dhctoken.h +++ b/sbin/dhclient/dhctoken.h @@ -129,6 +129,9 @@ #define AUTHORITATIVE 333 #define TOKEN_NOT 334 #define ALWAYS_REPLY_RFC1048 335 +#define VLAN_ID 336 +#define VLAN_PCP 337 +#define VLAN_PARENT 338 #define is_identifier(x) ((x) >= FIRST_TOKEN && \ (x) != STRING && \ diff --git a/sbin/dhclient/dispatch.c b/sbin/dhclient/dispatch.c index d493717e58a..b10ee70bf27 100644 --- a/sbin/dhclient/dispatch.c +++ b/sbin/dhclient/dispatch.c @@ -69,6 +69,7 @@ static int interface_status(struct interface_info *ifinfo); void discover_interfaces(struct interface_info *iface) { + char *pname = iface->client->config->vlan_parent; struct ifaddrs *ifap, *ifa; struct sockaddr_in foo; struct ifreq *tif; @@ -124,7 +125,20 @@ discover_interfaces(struct interface_info *iface) /* Register the interface... */ if_register_receive(iface); - if_register_send(iface); + if (pname != NULL) { + char rname[IFNAMSIZ]; + + /* Change interface name for bpf registration */ + strlcpy(rname, iface->name, IFNAMSIZ); + strlcpy(iface->ifp->ifr_name, pname, IFNAMSIZ); + + if_register_send(iface); + + /* Change name back to original */ + strlcpy(iface->ifp->ifr_name, rname, IFNAMSIZ); + } else { + if_register_send(iface); + } add_protocol(iface->name, iface->rfdesc, got_one, iface); freeifaddrs(ifap); } diff --git a/sbin/dhclient/packet.c b/sbin/dhclient/packet.c index 859f48b1a66..ecee69d44ea 100644 --- a/sbin/dhclient/packet.c +++ b/sbin/dhclient/packet.c @@ -92,19 +92,29 @@ void assemble_hw_header(struct interface_info *interface, unsigned char *buf, int *bufix) { - struct ether_header eh; + int vlpcp = interface->client->config->vlan_pcp; + int vlid = interface->client->config->vlan_id; + int plen = ETHER_HEADER_SIZE; + struct ether_vlan_header eh; - memset(eh.ether_dhost, 0xff, sizeof(eh.ether_dhost)); - if (interface->hw_address.hlen == sizeof(eh.ether_shost)) - memcpy(eh.ether_shost, interface->hw_address.haddr, - sizeof(eh.ether_shost)); + memset(eh.evl_dhost, 0xff, sizeof(eh.evl_dhost)); + if (interface->hw_address.hlen == sizeof(eh.evl_shost)) + memcpy(eh.evl_shost, interface->hw_address.haddr, + sizeof(eh.evl_shost)); else - memset(eh.ether_shost, 0x00, sizeof(eh.ether_shost)); + memset(eh.evl_shost, 0x00, sizeof(eh.evl_shost)); - eh.ether_type = htons(ETHERTYPE_IP); + eh.evl_encap_proto = htons(ETHERTYPE_IP); - memcpy(&buf[*bufix], &eh, ETHER_HEADER_SIZE); - *bufix += ETHER_HEADER_SIZE; + if (vlid != 0) { + eh.evl_tag = htons(EVL_MAKETAG(vlid, vlpcp, 0)); + eh.evl_encap_proto = htons(ETHERTYPE_VLAN); + eh.evl_proto = htons(ETHERTYPE_IP); + plen += ETHER_VLAN_ENCAP_LEN; + } + + memcpy(&buf[*bufix], &eh, plen); + *bufix += plen; } void