1 /* 2 * Copyright (C) 2010 Felix Fietkau <nbd@openwrt.org> 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License v2 as published by 6 * the Free Software Foundation. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 * 13 * You should have received a copy of the GNU General Public License 14 * along with this program; if not, write to the Free Software 15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 16 */ 17 18 #define _GNU_SOURCE 19 #include <sys/socket.h> 20 #include <stdio.h> 21 #include <string.h> 22 #include <errno.h> 23 #include <unistd.h> 24 #include <fcntl.h> 25 26 #include "relayd.h" 27 28 struct ip_packet { 29 struct ether_header eth; 30 struct iphdr iph; 31 } __packed; 32 33 34 enum { 35 DHCP_OPTION_ROUTER = 0x03, 36 DHCP_OPTION_ROUTES = 0x79, 37 DHCP_OPTION_END = 0xff, 38 }; 39 40 struct dhcp_option { 41 uint8_t code; 42 uint8_t len; 43 uint8_t data[]; 44 }; 45 46 struct dhcp_header { 47 uint8_t op, htype, hlen, hops; 48 uint32_t xit; 49 uint16_t secs, flags; 50 struct in_addr ciaddr, yiaddr, siaddr, giaddr; 51 unsigned char chaddr[16]; 52 unsigned char sname[64]; 53 unsigned char file[128]; 54 uint32_t cookie; 55 uint8_t option_data[]; 56 } __packed; 57 58 static uint16_t 59 chksum(uint16_t sum, const uint8_t *data, uint16_t len) 60 { 61 const uint8_t *last; 62 uint16_t t; 63 64 last = data + len - 1; 65 66 while(data < last) { 67 t = (data[0] << 8) + data[1]; 68 sum += t; 69 if(sum < t) 70 sum++; 71 data += 2; 72 } 73 74 if(data == last) { 75 t = (data[0] << 8) + 0; 76 sum += t; 77 if(sum < t) 78 sum++; 79 } 80 81 return sum; 82 } 83 84 static void 85 parse_dhcp_options(struct relayd_host *host, struct dhcp_header *dhcp, int len) 86 { 87 uint8_t *end = (uint8_t *) dhcp + len; 88 struct dhcp_option *opt = (void *)dhcp->option_data; 89 static const uint8_t dest[4] = { 0, 0, 0, 0 }; 90 91 while((uint8_t *) opt + sizeof(*opt) < end) { 92 if ((uint8_t *) opt + opt->len > end || 93 (uint8_t *) opt + sizeof(*opt) > end ) 94 break; 95 96 opt = (void *) &opt->data[opt->len]; 97 if ((uint8_t *) opt + sizeof(*opt) > end ) 98 break; 99 switch(opt->code) { 100 case DHCP_OPTION_ROUTER: 101 DPRINTF(2, "Found a DHCP router option, len=%d\n", opt->len); 102 if (!memcmp(opt->data, host->ipaddr, 4)) 103 relayd_add_host_route(host, dest, 0); 104 else 105 relayd_add_pending_route(opt->data, dest, 0, 10000); 106 break; 107 case DHCP_OPTION_ROUTES: 108 DPRINTF(2, "Found a DHCP static routes option, len=%d\n", opt->len); 109 break; 110 case DHCP_OPTION_END: 111 opt = (void *) end; 112 continue; 113 default: 114 DPRINTF(3, "Skipping unknown DHCP option %02x\n", opt->code); 115 continue; 116 } 117 118 } 119 } 120 121 bool relayd_handle_dhcp_packet(struct relayd_interface *rif, void *data, int len, bool forward, bool parse) 122 { 123 struct ip_packet *pkt = data; 124 struct udphdr *udp; 125 struct dhcp_header *dhcp; 126 struct relayd_host *host; 127 int udplen; 128 uint16_t sum; 129 130 if (pkt->eth.ether_type != htons(ETH_P_IP)) 131 return false; 132 133 if (pkt->iph.version != 4) 134 return false; 135 136 if (pkt->iph.protocol != IPPROTO_UDP) 137 return false; 138 139 udp = (void *) ((char *) &pkt->iph + (pkt->iph.ihl << 2)); 140 dhcp = (void *) (udp + 1); 141 142 if ((uint8_t *)udp + sizeof(*udp) > (uint8_t *)data + len || 143 (uint8_t *)dhcp + sizeof(*dhcp) > (uint8_t *)data + len) 144 return false; 145 146 udplen = ntohs(udp->len); 147 if (udplen > len - ((char *) udp - (char *) data)) 148 return false; 149 150 if (udp->dest != htons(67) && udp->source != htons(67)) 151 return false; 152 153 if (dhcp->op != 1 && dhcp->op != 2) 154 return false; 155 156 if (!forward) 157 return true; 158 159 if (dhcp->op == 2) { 160 host = relayd_refresh_host(rif, pkt->eth.ether_shost, (void *) &pkt->iph.saddr); 161 if (host && parse) 162 parse_dhcp_options(host, dhcp, udplen - sizeof(struct udphdr)); 163 } 164 165 DPRINTF(2, "%s: handling DHCP %s\n", rif->ifname, (dhcp->op == 1 ? "request" : "response")); 166 167 dhcp->flags |= htons(DHCP_FLAG_BROADCAST); 168 169 udp->check = 0; 170 sum = udplen + IPPROTO_UDP; 171 sum = chksum(sum, (void *) &pkt->iph.saddr, 8); 172 sum = chksum(sum, (void *) udp, udplen); 173 if (sum == 0) 174 sum = 0xffff; 175 176 udp->check = htons(~sum); 177 178 relayd_forward_bcast_packet(rif, data, len); 179 180 return true; 181 } 182 183 184
This page was automatically generated by LXR 0.3.1. • OpenWrt