diff --git a/sbin/dhclient/bpf.c b/sbin/dhclient/bpf.c index 7c2e531..e7787ba 100644 --- a/sbin/dhclient/bpf.c +++ b/sbin/dhclient/bpf.c @@ -96,24 +96,46 @@ 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[] = { - BPF_STMT(BPF_LD + BPF_B + BPF_IND, 14), - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, (IPVERSION << 4) + 5, 0, 12), - - /* 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), + /* + * BROKEN/WIP: must also check (and pass) if packet has 802.1q encapsulation + */ + BPF_STMT(BPF_RET+BPF_K, (u_int)-1), + + /* Set indexing offset to 0 */ + BPF_STMT(BPF_LDX + BPF_B + BPF_IMM, 0), + + /* Load Ethernet type */ + BPF_STMT(BPF_LD + BPF_H + BPF_IND, 12), + + /* Check if it is an IP packet */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_IP, 2, 0), + + /* If not, check if it is a 802.1q packet */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETHERTYPE_VLAN, 0, 10), + /* If it is, change offset for further tests */ + BPF_STMT(BPF_LDX + BPF_B + BPF_IMM, 4), + /* Make sure it's a UDP packet... */ - BPF_STMT(BPF_LD + BPF_B + BPF_ABS, 23), + BPF_STMT(BPF_LD + BPF_B + BPF_IND, 23), BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, IPPROTO_UDP, 0, 8), /* Make sure this isn't a fragment... */ - BPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20), + BPF_STMT(BPF_LD + BPF_H + BPF_IND, 20), BPF_JUMP(BPF_JMP + BPF_JSET + BPF_K, 0x1fff, 6, 0), /* patched */ + /* + * IP checks + */ + /* Move index to accumulator */ + BPF_STMT(BPF_MISC + BPF_TXA, 0), /* Get the IP header length... */ BPF_STMT(BPF_LDX + BPF_B + BPF_MSH, 14), - + /* Add it to the base offset */ + BPF_STMT(BPF_ALU + BPF_ADD + BPF_X, 0), + /* Store it in X */ + BPF_STMT(BPF_MISC + BPF_TAX, 0), + /* Make sure it's from the right port... */ BPF_STMT(BPF_LD + BPF_H + BPF_IND, 14), BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 68, 0, 3), @@ -154,11 +177,12 @@ 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); + /* Disabled as filter does not handle possible 802.1q encapsulation */ + //if (dhcp_bpf_wfilter[5].k == 0x1fff) + // dhcp_bpf_wfilter[5].k = htons(IP_MF|IP_OFFMASK); - if (ioctl(info->wfdesc, BIOCSETWF, &p) < 0) - error("Can't install write filter program: %m"); + //if (ioctl(info->wfdesc, BIOCSETWF, &p) < 0) + // error("Can't install write filter program: %m"); if (ioctl(info->wfdesc, BIOCLOCK, NULL) < 0) error("Cannot lock bpf"); @@ -175,6 +199,9 @@ if_register_send(struct interface_info *info) if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) == -1) error("setsockopt(IP_HDRINCL): %m"); + + note("Interface %s attached to bpf for sending", info->ifp->ifr_name); + info->ufdesc = sock; } @@ -274,6 +301,9 @@ if_register_receive(struct interface_info *info) error("Can't limit bpf descriptor: %m"); if (cap_ioctls_limit(info->rfdesc, cmds, 2) < 0 && errno != ENOSYS) error("Can't limit ioctls for bpf descriptor: %m"); + + note("Interface %s attached to bpf for receiving", info->ifp->ifr_name); + } void diff --git a/sbin/dhclient/clparse.c b/sbin/dhclient/clparse.c index 4f234c7..1c85629 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.send_interface = NULL; top_level_config.timeout = 60; top_level_config.select_interval = 0; top_level_config.reboot_timeout = 10; @@ -186,6 +189,7 @@ read_client_leases(void) * RETRY number | * REBOOT number | * SELECT_TIMEOUT number | + * PRIORITY number | * SCRIPT string | * interface-declaration | * LEASE client-lease-statement | @@ -198,7 +202,8 @@ 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: parse_option_decl(cfile, &config->send_options[0]); @@ -257,6 +262,17 @@ parse_client_statement(FILE *cfile, struct interface_info *ip, case REBOOT: parse_lease_time(cfile, &config->reboot_timeout); return; + case VLAN_PCP: + parse_lease_time(cfile, &tmp); + config->vlan_pcp = (int)tmp; + return; + case VLAN_ID: + parse_lease_time(cfile, &tmp); + config->vlan_id = (int)tmp; + return; + case SEND_INTERFACE: + config->send_interface = parse_string(cfile); + 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 3c8932d..e8774fe 100644 --- a/sbin/dhclient/conflex.c +++ b/sbin/dhclient/conflex.c @@ -449,7 +449,6 @@ intern(char *atom, int dfv) return (PREPEND); if (!strcasecmp(atom + 1, "acket")) return (PACKET); - break; case 'r': if (!strcasecmp(atom + 1, "ange")) return (RANGE); @@ -467,10 +466,14 @@ intern(char *atom, int dfv) return (REBOOT); if (!strcasecmp(atom + 1, "eject")) return (REJECT); + if (!strcasecmp(atom + 1, "fc3118-auth")) + return (REJECT); break; case 's': if (!strcasecmp(atom + 1, "earch")) return (SEARCH); + if (!strcasecmp(atom + 1, "end-interface")) + return (SEND_INTERFACE); if (!strcasecmp(atom + 1, "tarts")) return (STARTS); if (!strcasecmp(atom + 1, "iaddr")) @@ -519,7 +522,11 @@ intern(char *atom, int dfv) case 'v': if (!strcasecmp(atom + 1, "endor-class")) return (VENDOR_CLASS); - break; + if (!strcasecmp(atom + 1, "lan-id")) + return (VLAN_ID); + if (!strcasecmp(atom + 1, "lan-pcp")) + return (VLAN_PCP); + break; case 'y': if (!strcasecmp(atom + 1, "iaddr")) return (YIADDR); diff --git a/sbin/dhclient/dhcp.h b/sbin/dhclient/dhcp.h index 6e7d1a7..817dcea 100644 --- a/sbin/dhclient/dhcp.h +++ b/sbin/dhclient/dhcp.h @@ -169,6 +169,7 @@ struct dhcp_packet { #define DHO_STREETTALK_SERVER 75 #define DHO_STREETTALK_DA_SERVER 76 #define DHO_DHCP_USER_CLASS_ID 77 +#define DHO_RFC3118_AUTH 90 #define DHO_DOMAIN_SEARCH 119 #define DHO_CLASSLESS_ROUTES 121 #define DHO_END 255 diff --git a/sbin/dhclient/dhcpd.h b/sbin/dhclient/dhcpd.h index 479753e..cb4c489 100644 --- a/sbin/dhclient/dhcpd.h +++ b/sbin/dhclient/dhcpd.h @@ -153,7 +153,10 @@ struct client_config { struct option_data send_options[256]; u_int8_t required_options[256]; u_int8_t requested_options[256]; - int requested_option_count; + int requested_option_count; + int vlan_id; + int vlan_pcp; + char *send_interface; time_t timeout; time_t initial_interval; time_t retry_interval; @@ -310,6 +313,7 @@ ssize_t receive_packet(struct interface_info *, unsigned char *, size_t, /* dispatch.c */ extern void (*bootp_packet_handler)(struct interface_info *, struct dhcp_packet *, int, unsigned int, struct iaddr, struct hardware *); +void discover_interface(struct interface_info *); void discover_interfaces(struct interface_info *); void reinitialize_interfaces(void); void dispatch(void); diff --git a/sbin/dhclient/dhctoken.h b/sbin/dhclient/dhctoken.h index 7b23242..8e6d5ab 100644 --- a/sbin/dhclient/dhctoken.h +++ b/sbin/dhclient/dhctoken.h @@ -129,6 +129,10 @@ #define AUTHORITATIVE 333 #define TOKEN_NOT 334 #define ALWAYS_REPLY_RFC1048 335 +#define VLAN_ID 336 +#define VLAN_PCP 337 +#define SEND_INTERFACE 338 +#define RFC3118_AUTH 339 #define is_identifier(x) ((x) >= FIRST_TOKEN && \ (x) != STRING && \ diff --git a/sbin/dhclient/dispatch.c b/sbin/dhclient/dispatch.c index 3ee0cf6..080a3f3 100644 --- a/sbin/dhclient/dispatch.c +++ b/sbin/dhclient/dispatch.c @@ -121,12 +122,44 @@ discover_interfaces(struct interface_info *iface) if (!iface->ifp) error("%s: not found", iface->name); + freeifaddrs(ifap); +} - /* Register the interface... */ + +void +discover_interfaces(struct interface_info *iface) +{ + char *sname = iface->client->config->send_interface; + char *rname; + + strcpy(rname, iface->name); + + /* Discover the receiving interface... */ + note("Registering receive interface: %s", iface->name); + discover_interface(iface); if_register_receive(iface); - if_register_send(iface); add_protocol(iface->name, iface->rfdesc, got_one, iface); - freeifaddrs(ifap); + + /* Register the sending interface... */ + if (sname != NULL) { + note("Registering sending interface: %s", sname); + if (iface->client->config->vlan_id !=0) + note(" VLAN ID: %d, VLAN PCP: %d", + iface->client->config->vlan_id, iface->client->config->vlan_pcp); + + /* Change interface name for bpf registration */ + strlcpy(iface->ifp->ifr_name, sname, IFNAMSIZ); + + if_register_send(iface); + + /* Change name back to original */ + strlcpy(iface->ifp->ifr_name, rname, IFNAMSIZ); + } + else { + note("Registering same interface for sending"); + if_register_send(iface); + } + } void diff --git a/sbin/dhclient/packet.c b/sbin/dhclient/packet.c index f79ca2f..bd68c34 100644 --- a/sbin/dhclient/packet.c +++ b/sbin/dhclient/packet.c @@ -49,6 +49,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #define ETHER_HEADER_SIZE (ETHER_ADDR_LEN * 2 + sizeof(u_int16_t)) @@ -93,18 +94,46 @@ assemble_hw_header(struct interface_info *interface, unsigned char *buf, int *bufix) { struct ether_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)); - else - memset(eh.ether_shost, 0x00, sizeof(eh.ether_shost)); - - eh.ether_type = htons(ETHERTYPE_IP); - - memcpy(&buf[*bufix], &eh, ETHER_HEADER_SIZE); - *bufix += ETHER_HEADER_SIZE; + struct ether_vlan_header vh; + + int vlid = interface->client->config->vlan_id; + int vlpcp = interface->client->config->vlan_pcp; + + /* If vlan id is not default, use a 802.1q VLAN frame */ + if (vlid != 0) { + memset(vh.evl_dhost, 0xff, sizeof(vh.evl_dhost)); + if (interface->hw_address.hlen == sizeof(eh.ether_shost)) + memcpy(vh.evl_shost, interface->hw_address.haddr, + sizeof(vh.evl_shost)); + else + memset(vh.evl_shost, 0x00, sizeof(vh.evl_shost)); + + /* Set encapsulation header type to 802.1q */ + vh.evl_encap_proto = htons(ETHERTYPE_VLAN); + + /* Add VLAN tag */ + vh.evl_tag = htons(EVL_MAKETAG(vlid, vlpcp, 0)); /* VLID, PRI, CFI */ + + /* Set IP ethernet type tag */ + vh.evl_proto = htons(ETHERTYPE_IP); + + memcpy(&buf[*bufix], &vh, sizeof(vh)); + *bufix += sizeof(vh); + + } + else { + /* Standard IPv4 header */ + 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)); + else + memset(eh.ether_shost, 0x00, sizeof(eh.ether_shost)); + + eh.ether_type = htons(ETHERTYPE_IP); + memcpy(&buf[*bufix], &eh, ETHER_HEADER_SIZE); + *bufix += ETHER_HEADER_SIZE; + } } void diff --git a/sbin/dhclient/tables.c b/sbin/dhclient/tables.c index c7bac57..bee776c 100644 --- a/sbin/dhclient/tables.c +++ b/sbin/dhclient/tables.c @@ -155,7 +155,7 @@ struct option dhcp_options[256] = { { "nds-context", "X", &dhcp_universe, 87 }, { "option-88", "X", &dhcp_universe, 88 }, { "option-89", "X", &dhcp_universe, 89 }, - { "option-90", "X", &dhcp_universe, 90 }, + { "rfc3118-auth", "X", &dhcp_universe, 90 }, { "option-91", "X", &dhcp_universe, 91 }, { "option-92", "X", &dhcp_universe, 92 }, { "option-93", "X", &dhcp_universe, 93 }, @@ -335,6 +335,8 @@ unsigned char dhcp_option_default_priority_list[] = { DHO_DHCP_REBINDING_TIME, DHO_DHCP_CLASS_IDENTIFIER, DHO_DHCP_CLIENT_IDENTIFIER, + DHO_DHCP_USER_CLASS_ID, + DHO_RFC3118_AUTH, DHO_SUBNET_MASK, DHO_TIME_OFFSET, DHO_CLASSLESS_ROUTES,