| /* |
| * OpenVPN -- An application to securely tunnel IP networks |
| * over a single TCP/UDP port, with support for SSL/TLS-based |
| * session authentication and key exchange, |
| * packet encryption, packet authentication, and |
| * packet compression. |
| * |
| * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net> |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 |
| * as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License along |
| * with this program; if not, write to the Free Software Foundation, Inc., |
| * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #elif defined(_MSC_VER) |
| #include "config-msvc.h" |
| #endif |
| |
| #include "syshead.h" |
| |
| #include "dhcp.h" |
| #include "socket.h" |
| #include "error.h" |
| |
| #include "memdbg.h" |
| |
| static int |
| get_dhcp_message_type(const struct dhcp *dhcp, const int optlen) |
| { |
| const uint8_t *p = (uint8_t *) (dhcp + 1); |
| int i; |
| |
| for (i = 0; i < optlen; ++i) |
| { |
| const uint8_t type = p[i]; |
| const int room = optlen - i; |
| if (type == DHCP_END) /* didn't find what we were looking for */ |
| { |
| return -1; |
| } |
| else if (type == DHCP_PAD) /* no-operation */ |
| { |
| } |
| else if (type == DHCP_MSG_TYPE) /* what we are looking for */ |
| { |
| if (room >= 3) |
| { |
| if (p[i+1] == 1) /* option length should be 1 */ |
| { |
| return p[i+2]; /* return message type */ |
| } |
| } |
| return -1; |
| } |
| else /* some other option */ |
| { |
| if (room >= 2) |
| { |
| const int len = p[i+1]; /* get option length */ |
| i += (len + 1); /* advance to next option */ |
| } |
| } |
| } |
| return -1; |
| } |
| |
| static in_addr_t |
| do_extract(struct dhcp *dhcp, int optlen) |
| { |
| uint8_t *p = (uint8_t *) (dhcp + 1); |
| int i; |
| in_addr_t ret = 0; |
| |
| for (i = 0; i < optlen; ) |
| { |
| const uint8_t type = p[i]; |
| const int room = optlen - i; |
| if (type == DHCP_END) |
| { |
| break; |
| } |
| else if (type == DHCP_PAD) |
| { |
| ++i; |
| } |
| else if (type == DHCP_ROUTER) |
| { |
| if (room >= 2) |
| { |
| const int len = p[i+1]; /* get option length */ |
| if (len <= (room-2)) |
| { |
| /* get router IP address */ |
| if (!ret && len >= 4 && (len & 3) == 0) |
| { |
| memcpy(&ret, p+i+2, 4); |
| ret = ntohl(ret); |
| } |
| { |
| /* delete the router option */ |
| uint8_t *dest = p + i; |
| const int owlen = len + 2; /* len of data to overwrite */ |
| uint8_t *src = dest + owlen; |
| uint8_t *end = p + optlen; |
| const int movlen = end - src; |
| if (movlen > 0) |
| { |
| memmove(dest, src, movlen); /* overwrite router option */ |
| } |
| memset(end - owlen, DHCP_PAD, owlen); /* pad tail */ |
| } |
| } |
| else |
| { |
| break; |
| } |
| } |
| else |
| { |
| break; |
| } |
| } |
| else /* some other option */ |
| { |
| if (room >= 2) |
| { |
| const int len = p[i+1]; /* get option length */ |
| i += (len + 2); /* advance to next option */ |
| } |
| else |
| { |
| break; |
| } |
| } |
| } |
| return ret; |
| } |
| |
| static uint16_t |
| udp_checksum(const uint8_t *buf, |
| const int len_udp, |
| const uint8_t *src_addr, |
| const uint8_t *dest_addr) |
| { |
| uint16_t word16; |
| uint32_t sum = 0; |
| int i; |
| |
| /* make 16 bit words out of every two adjacent 8 bit words and */ |
| /* calculate the sum of all 16 bit words */ |
| for (i = 0; i < len_udp; i += 2) |
| { |
| word16 = ((buf[i] << 8) & 0xFF00) + ((i + 1 < len_udp) ? (buf[i+1] & 0xFF) : 0); |
| sum += word16; |
| } |
| |
| /* add the UDP pseudo header which contains the IP source and destination addresses */ |
| for (i = 0; i < 4; i += 2) |
| { |
| word16 = ((src_addr[i] << 8) & 0xFF00) + (src_addr[i+1] & 0xFF); |
| sum += word16; |
| } |
| for (i = 0; i < 4; i += 2) |
| { |
| word16 = ((dest_addr[i] << 8) & 0xFF00) + (dest_addr[i+1] & 0xFF); |
| sum += word16; |
| } |
| |
| /* the protocol number and the length of the UDP packet */ |
| sum += (uint16_t) OPENVPN_IPPROTO_UDP + (uint16_t) len_udp; |
| |
| /* keep only the last 16 bits of the 32 bit calculated sum and add the carries */ |
| while (sum >> 16) |
| { |
| sum = (sum & 0xFFFF) + (sum >> 16); |
| } |
| |
| /* Take the one's complement of sum */ |
| return ((uint16_t) ~sum); |
| } |
| |
| in_addr_t |
| dhcp_extract_router_msg(struct buffer *ipbuf) |
| { |
| struct dhcp_full *df = (struct dhcp_full *) BPTR(ipbuf); |
| const int optlen = BLEN(ipbuf) - (sizeof(struct openvpn_iphdr) + sizeof(struct openvpn_udphdr) + sizeof(struct dhcp)); |
| |
| if (optlen >= 0 |
| && df->ip.protocol == OPENVPN_IPPROTO_UDP |
| && df->udp.source == htons(BOOTPS_PORT) |
| && df->udp.dest == htons(BOOTPC_PORT) |
| && df->dhcp.op == BOOTREPLY) |
| { |
| const int message_type = get_dhcp_message_type(&df->dhcp, optlen); |
| if (message_type == DHCPACK || message_type == DHCPOFFER) |
| { |
| /* get the router IP address while padding out all DHCP router options */ |
| const in_addr_t ret = do_extract(&df->dhcp, optlen); |
| |
| /* recompute the UDP checksum */ |
| df->udp.check = 0; |
| df->udp.check = htons(udp_checksum((uint8_t *) &df->udp, |
| sizeof(struct openvpn_udphdr) + sizeof(struct dhcp) + optlen, |
| (uint8_t *)&df->ip.saddr, |
| (uint8_t *)&df->ip.daddr)); |
| |
| /* only return the extracted Router address if DHCPACK */ |
| if (message_type == DHCPACK) |
| { |
| if (ret) |
| { |
| struct gc_arena gc = gc_new(); |
| msg(D_ROUTE, "Extracted DHCP router address: %s", print_in_addr_t(ret, 0, &gc)); |
| gc_free(&gc); |
| } |
| |
| return ret; |
| } |
| } |
| } |
| return 0; |
| } |