diff options
Diffstat (limited to 'networking/udhcp/dhcpc.c')
-rw-r--r-- | networking/udhcp/dhcpc.c | 121 |
1 files changed, 73 insertions, 48 deletions
diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c index 8dee916d9..7dfc160e2 100644 --- a/networking/udhcp/dhcpc.c +++ b/networking/udhcp/dhcpc.c | |||
@@ -976,56 +976,13 @@ static int udhcp_raw_socket(int ifindex) | |||
976 | int fd; | 976 | int fd; |
977 | struct sockaddr_ll sock; | 977 | struct sockaddr_ll sock; |
978 | 978 | ||
979 | /* | ||
980 | * Comment: | ||
981 | * | ||
982 | * I've selected not to see LL header, so BPF doesn't see it, too. | ||
983 | * The filter may also pass non-IP and non-ARP packets, but we do | ||
984 | * a more complete check when receiving the message in userspace. | ||
985 | * | ||
986 | * and filter shamelessly stolen from: | ||
987 | * | ||
988 | * http://www.flamewarmaster.de/software/dhcpclient/ | ||
989 | * | ||
990 | * There are a few other interesting ideas on that page (look under | ||
991 | * "Motivation"). Use of netlink events is most interesting. Think | ||
992 | * of various network servers listening for events and reconfiguring. | ||
993 | * That would obsolete sending HUP signals and/or make use of restarts. | ||
994 | * | ||
995 | * Copyright: 2006, 2007 Stefan Rompf <sux@loplof.de>. | ||
996 | * License: GPL v2. | ||
997 | * | ||
998 | * TODO: make conditional? | ||
999 | */ | ||
1000 | static const struct sock_filter filter_instr[] = { | ||
1001 | /* load 9th byte (protocol) */ | ||
1002 | BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 9), | ||
1003 | /* jump to L1 if it is IPPROTO_UDP, else to L4 */ | ||
1004 | BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, IPPROTO_UDP, 0, 6), | ||
1005 | /* L1: load halfword from offset 6 (flags and frag offset) */ | ||
1006 | BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 6), | ||
1007 | /* jump to L4 if any bits in frag offset field are set, else to L2 */ | ||
1008 | BPF_JUMP(BPF_JMP|BPF_JSET|BPF_K, 0x1fff, 4, 0), | ||
1009 | /* L2: skip IP header (load index reg with header len) */ | ||
1010 | BPF_STMT(BPF_LDX|BPF_B|BPF_MSH, 0), | ||
1011 | /* load udp destination port from halfword[header_len + 2] */ | ||
1012 | BPF_STMT(BPF_LD|BPF_H|BPF_IND, 2), | ||
1013 | /* jump to L3 if udp dport is CLIENT_PORT, else to L4 */ | ||
1014 | BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 68, 0, 1), | ||
1015 | /* L3: accept packet */ | ||
1016 | BPF_STMT(BPF_RET|BPF_K, 0xffffffff), | ||
1017 | /* L4: discard packet */ | ||
1018 | BPF_STMT(BPF_RET|BPF_K, 0), | ||
1019 | }; | ||
1020 | static const struct sock_fprog filter_prog = { | ||
1021 | .len = sizeof(filter_instr) / sizeof(filter_instr[0]), | ||
1022 | /* casting const away: */ | ||
1023 | .filter = (struct sock_filter *) filter_instr, | ||
1024 | }; | ||
1025 | |||
1026 | log1("Opening raw socket on ifindex %d", ifindex); //log2? | 979 | log1("Opening raw socket on ifindex %d", ifindex); //log2? |
1027 | 980 | ||
1028 | fd = xsocket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP)); | 981 | fd = xsocket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP)); |
982 | /* ^^^^^ | ||
983 | * SOCK_DGRAM: remove link-layer headers on input (SOCK_RAW keeps them) | ||
984 | * ETH_P_IP: want to receive only packets with IPv4 eth type | ||
985 | */ | ||
1029 | log1("Got raw socket fd"); //log2? | 986 | log1("Got raw socket fd"); //log2? |
1030 | 987 | ||
1031 | sock.sll_family = AF_PACKET; | 988 | sock.sll_family = AF_PACKET; |
@@ -1033,13 +990,58 @@ static int udhcp_raw_socket(int ifindex) | |||
1033 | sock.sll_ifindex = ifindex; | 990 | sock.sll_ifindex = ifindex; |
1034 | xbind(fd, (struct sockaddr *) &sock, sizeof(sock)); | 991 | xbind(fd, (struct sockaddr *) &sock, sizeof(sock)); |
1035 | 992 | ||
993 | #if 0 /* Several users reported breakage when BPF filter is used */ | ||
1036 | if (CLIENT_PORT == 68) { | 994 | if (CLIENT_PORT == 68) { |
1037 | /* Use only if standard port is in use */ | 995 | /* Use only if standard port is in use */ |
996 | /* | ||
997 | * I've selected not to see LL header, so BPF doesn't see it, too. | ||
998 | * The filter may also pass non-IP and non-ARP packets, but we do | ||
999 | * a more complete check when receiving the message in userspace. | ||
1000 | * | ||
1001 | * and filter shamelessly stolen from: | ||
1002 | * | ||
1003 | * http://www.flamewarmaster.de/software/dhcpclient/ | ||
1004 | * | ||
1005 | * There are a few other interesting ideas on that page (look under | ||
1006 | * "Motivation"). Use of netlink events is most interesting. Think | ||
1007 | * of various network servers listening for events and reconfiguring. | ||
1008 | * That would obsolete sending HUP signals and/or make use of restarts. | ||
1009 | * | ||
1010 | * Copyright: 2006, 2007 Stefan Rompf <sux@loplof.de>. | ||
1011 | * License: GPL v2. | ||
1012 | */ | ||
1013 | static const struct sock_filter filter_instr[] = { | ||
1014 | /* load 9th byte (protocol) */ | ||
1015 | BPF_STMT(BPF_LD|BPF_B|BPF_ABS, 9), | ||
1016 | /* jump to L1 if it is IPPROTO_UDP, else to L4 */ | ||
1017 | BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, IPPROTO_UDP, 0, 6), | ||
1018 | /* L1: load halfword from offset 6 (flags and frag offset) */ | ||
1019 | BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 6), | ||
1020 | /* jump to L4 if any bits in frag offset field are set, else to L2 */ | ||
1021 | BPF_JUMP(BPF_JMP|BPF_JSET|BPF_K, 0x1fff, 4, 0), | ||
1022 | /* L2: skip IP header (load index reg with header len) */ | ||
1023 | BPF_STMT(BPF_LDX|BPF_B|BPF_MSH, 0), | ||
1024 | /* load udp destination port from halfword[header_len + 2] */ | ||
1025 | BPF_STMT(BPF_LD|BPF_H|BPF_IND, 2), | ||
1026 | /* jump to L3 if udp dport is CLIENT_PORT, else to L4 */ | ||
1027 | BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 68, 0, 1), | ||
1028 | /* L3: accept packet ("accept 0x7fffffff bytes") */ | ||
1029 | /* Accepting 0xffffffff works too but kernel 2.6.19 is buggy */ | ||
1030 | BPF_STMT(BPF_RET|BPF_K, 0x7fffffff), | ||
1031 | /* L4: discard packet ("accept zero bytes") */ | ||
1032 | BPF_STMT(BPF_RET|BPF_K, 0), | ||
1033 | }; | ||
1034 | static const struct sock_fprog filter_prog = { | ||
1035 | .len = sizeof(filter_instr) / sizeof(filter_instr[0]), | ||
1036 | /* casting const away: */ | ||
1037 | .filter = (struct sock_filter *) filter_instr, | ||
1038 | }; | ||
1038 | /* Ignoring error (kernel may lack support for this) */ | 1039 | /* Ignoring error (kernel may lack support for this) */ |
1039 | if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog, | 1040 | if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog, |
1040 | sizeof(filter_prog)) >= 0) | 1041 | sizeof(filter_prog)) >= 0) |
1041 | log1("Attached filter to raw socket fd"); // log? | 1042 | log1("Attached filter to raw socket fd"); // log? |
1042 | } | 1043 | } |
1044 | #endif | ||
1043 | 1045 | ||
1044 | if (setsockopt(fd, SOL_PACKET, PACKET_AUXDATA, | 1046 | if (setsockopt(fd, SOL_PACKET, PACKET_AUXDATA, |
1045 | &const_int_1, sizeof(int)) < 0 | 1047 | &const_int_1, sizeof(int)) < 0 |
@@ -1230,7 +1232,7 @@ static void client_background(void) | |||
1230 | int udhcpc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 1232 | int udhcpc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
1231 | int udhcpc_main(int argc UNUSED_PARAM, char **argv) | 1233 | int udhcpc_main(int argc UNUSED_PARAM, char **argv) |
1232 | { | 1234 | { |
1233 | uint8_t *temp, *message; | 1235 | uint8_t *message; |
1234 | const char *str_V, *str_h, *str_F, *str_r; | 1236 | const char *str_V, *str_h, *str_F, *str_r; |
1235 | IF_FEATURE_UDHCP_PORT(char *str_P;) | 1237 | IF_FEATURE_UDHCP_PORT(char *str_P;) |
1236 | void *clientid_mac_ptr; | 1238 | void *clientid_mac_ptr; |
@@ -1638,6 +1640,8 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) | |||
1638 | case INIT_SELECTING: | 1640 | case INIT_SELECTING: |
1639 | /* Must be a DHCPOFFER */ | 1641 | /* Must be a DHCPOFFER */ |
1640 | if (*message == DHCPOFFER) { | 1642 | if (*message == DHCPOFFER) { |
1643 | uint8_t *temp; | ||
1644 | |||
1641 | /* What exactly is server's IP? There are several values. | 1645 | /* What exactly is server's IP? There are several values. |
1642 | * Example DHCP offer captured with tchdump: | 1646 | * Example DHCP offer captured with tchdump: |
1643 | * | 1647 | * |
@@ -1687,6 +1691,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) | |||
1687 | if (*message == DHCPACK) { | 1691 | if (*message == DHCPACK) { |
1688 | uint32_t lease_seconds; | 1692 | uint32_t lease_seconds; |
1689 | struct in_addr temp_addr; | 1693 | struct in_addr temp_addr; |
1694 | uint8_t *temp; | ||
1690 | 1695 | ||
1691 | temp = udhcp_get_option(&packet, DHCP_LEASE_TIME); | 1696 | temp = udhcp_get_option(&packet, DHCP_LEASE_TIME); |
1692 | if (!temp) { | 1697 | if (!temp) { |
@@ -1764,6 +1769,26 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) | |||
1764 | continue; /* back to main loop */ | 1769 | continue; /* back to main loop */ |
1765 | } | 1770 | } |
1766 | if (*message == DHCPNAK) { | 1771 | if (*message == DHCPNAK) { |
1772 | /* If network has more than one DHCP server, | ||
1773 | * "wrong" server can reply first, with a NAK. | ||
1774 | * Do not interpret it as a NAK from "our" server. | ||
1775 | */ | ||
1776 | if (server_addr != 0) { | ||
1777 | uint32_t svid; | ||
1778 | uint8_t *temp; | ||
1779 | |||
1780 | temp = udhcp_get_option(&packet, DHCP_SERVER_ID); | ||
1781 | if (!temp) { | ||
1782 | non_matching_svid: | ||
1783 | log1("%s with wrong server ID, ignoring packet", | ||
1784 | "Received DHCP NAK" | ||
1785 | ); | ||
1786 | continue; | ||
1787 | } | ||
1788 | move_from_unaligned32(svid, temp); | ||
1789 | if (svid != server_addr) | ||
1790 | goto non_matching_svid; | ||
1791 | } | ||
1767 | /* return to init state */ | 1792 | /* return to init state */ |
1768 | bb_info_msg("Received DHCP NAK"); | 1793 | bb_info_msg("Received DHCP NAK"); |
1769 | udhcp_run_script(&packet, "nak"); | 1794 | udhcp_run_script(&packet, "nak"); |