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 @@ -87,7 +87,7 @@ if_register_bpf(struct interface_info *info, int flags) if (ioctl(sock, BIOCSETIF, info->ifp) < 0) error("Can't attach interface %s to bpf device %s: %m", info->name, filename); - + return (sock); } @@ -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), @@ -127,6 +149,7 @@ struct bpf_insn dhcp_bpf_wfilter[] = { /* Otherwise, drop it. */ BPF_STMT(BPF_RET+BPF_K, 0), + }; int dhcp_bpf_wfilter_len = sizeof(dhcp_bpf_wfilter) / sizeof(struct bpf_insn); @@ -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 @@ -283,7 +313,7 @@ send_packet_unpriv(int privfd, struct dhcp_packet *raw, size_t len, struct imsg_hdr hdr; struct buf *buf; int errs; - + hdr.code = IMSG_SEND_PACKET; hdr.len = sizeof(hdr) + sizeof(size_t) + len + @@ -328,21 +358,22 @@ send_packet_priv(struct interface_info *interface, struct imsg_hdr *hdr, int fd) buf_read(fd, &raw, len); buf_read(fd, &from, sizeof(from)); buf_read(fd, &to, sizeof(to)); - + /* Assemble the headers... */ if (to.s_addr == INADDR_BROADCAST) assemble_hw_header(interface, buf, &bufp); assemble_udp_ip_header(buf, &bufp, from.s_addr, to.s_addr, htons(REMOTE_PORT), (unsigned char *)&raw, len); - + iov[0].iov_base = buf; iov[0].iov_len = bufp; iov[1].iov_base = &raw; iov[1].iov_len = len; /* Fire it off */ - if (to.s_addr == INADDR_BROADCAST) + if (to.s_addr == INADDR_BROADCAST) { result = writev(interface->wfdesc, iov, 2); + } else { struct sockaddr_in sato; @@ -355,7 +386,7 @@ send_packet_priv(struct interface_info *interface, struct imsg_hdr *hdr, int fd) msg.msg_name = (struct sockaddr *)&sato; msg.msg_namelen = sizeof(sato); msg.msg_iov = iov; - msg.msg_iovlen = 2; + msg.msg_iovlen = 2; result = sendmsg(interface->ufdesc, &msg, 0); } 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/dhclient.c b/sbin/dhclient/dhclient.c index 97f9d47..f0098c7 100644 --- a/sbin/dhclient/dhclient.c +++ b/sbin/dhclient/dhclient.c @@ -466,7 +466,7 @@ main(int argc, char *argv[]) priv_script_go(); /* set up the interface */ - discover_interfaces(ifi); + discover_interfaces(ifi); if (pipe(pipe_fd) == -1) error("pipe"); @@ -1254,7 +1254,7 @@ again: ip->name, inet_ntoa(inaddr_broadcast), REMOTE_PORT, (int)ip->client->interval); - /* Send out a packet. */ + /* Send out a packet. */ send_packet_unpriv(privfd, &ip->client->packet, ip->client->packet_length, inaddr_any, inaddr_broadcast); @@ -1479,7 +1479,7 @@ cancel: note("DHCPREQUEST on %s to %s port %d", ip->name, inet_ntoa(to), REMOTE_PORT); - /* Send out a packet. */ + /* Send out a packet. */ send_packet_unpriv(privfd, &ip->client->packet, ip->client->packet_length, from, to); @@ -2730,9 +2730,9 @@ fork_privchld(int fd, int fd2) switch (fork()) { case -1: error("cannot fork"); - case 0: + case 0: break; - default: + default: return (0); } @@ -2756,7 +2756,6 @@ fork_privchld(int fd, int fd2) if (nfds == 0 || !(pfd[0].revents & POLLIN)) continue; - dispatch_imsg(ifi, fd); } } 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 @@ -66,13 +66,13 @@ static int interface_status(struct interface_info *ifinfo); * register that interface with the network I/O software, figure out * what subnet it's on, and add it to the list of interfaces. */ -void -discover_interfaces(struct interface_info *iface) + void +discover_interface(struct interface_info *iface) { struct ifaddrs *ifap, *ifa; struct sockaddr_in foo; struct ifreq *tif; - + if (getifaddrs(&ifap) != 0) error("getifaddrs failed"); @@ -82,29 +82,30 @@ discover_interfaces(struct interface_info *iface) (!(ifa->ifa_flags & IFF_UP))) continue; - if (strcmp(iface->name, ifa->ifa_name)) + if (strcmp(iface->name, ifa->ifa_name)) { continue; - + } /* * If we have the capability, extract link information * and record it in a linked list. */ - if (ifa->ifa_addr->sa_family == AF_LINK) { + if (ifa->ifa_addr->sa_family == AF_LINK) { struct sockaddr_dl *foo = (struct sockaddr_dl *)ifa->ifa_addr; - + iface->index = foo->sdl_index; iface->hw_address.hlen = foo->sdl_alen; iface->hw_address.htype = HTYPE_ETHER; /* XXX */ memcpy(iface->hw_address.haddr, LLADDR(foo), foo->sdl_alen); - } else if (ifa->ifa_addr->sa_family == AF_INET) { + + } else if (ifa->ifa_addr->sa_family == AF_INET) { struct iaddr addr; - + memcpy(&foo, ifa->ifa_addr, sizeof(foo)); if (foo.sin_addr.s_addr == htonl(INADDR_LOOPBACK)) continue; - if (!iface->ifp) { + if (!iface->ifp) { int len = IFNAMSIZ + ifa->ifa_addr->sa_len; if ((tif = malloc(len)) == NULL) error("no space to remember ifp"); @@ -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 @@ -127,7 +156,7 @@ assemble_udp_ip_header(unsigned char *buf, int *bufix, u_int32_t from, ip.ip_dst.s_addr = to; ip.ip_sum = wrapsum(checksum((unsigned char *)&ip, sizeof(ip), 0)); - + /* * While the BPF -- used for broadcasts -- expects a "true" IP header * with all the bytes in network byte order, the raw socket interface @@ -145,7 +174,7 @@ assemble_udp_ip_header(unsigned char *buf, int *bufix, u_int32_t from, udp.uh_dport = port; /* XXX */ udp.uh_ulen = htons(sizeof(udp) + len); memset(&udp.uh_sum, 0, sizeof(udp.uh_sum)); - + udp.uh_sum = wrapsum(checksum((unsigned char *)&udp, sizeof(udp), checksum(data, len, checksum((unsigned char *)&ip.ip_src, 2 * sizeof(ip.ip_src), 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,