1/* serverpacket.c 2 * 3 * Constuct and send DHCP server packets 4 * 5 * Russ Dill <Russ.Dill@asu.edu> July 2001 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 */ 21 22#include <sys/socket.h> 23#include <netinet/in.h> 24#include <arpa/inet.h> 25#include <string.h> 26#include <time.h> 27 28#include "packet.h" 29#include "debug.h" 30#include "dhcpd.h" 31#include "options.h" 32#include "leases.h" 33 34/* send a packet to giaddr using the kernel ip stack */ 35static int send_packet_to_relay(struct dhcpMessage *payload) 36{ 37 DEBUG(LOG_INFO, "Forwarding packet to relay"); 38 39 return kernel_packet(payload, server_config.server, SERVER_PORT, 40 payload->giaddr, SERVER_PORT); 41} 42 43 44/* send a packet to a specific arp address and ip address by creating our own ip packet */ 45static int send_packet_to_client(struct dhcpMessage *payload, int force_broadcast) 46{ 47 unsigned char *chaddr; 48 u_int32_t ciaddr; 49 50 if (force_broadcast) { 51 DEBUG(LOG_INFO, "broadcasting packet to client (NAK)"); 52 ciaddr = INADDR_BROADCAST; 53 chaddr = MAC_BCAST_ADDR; 54 } else if (payload->ciaddr) { 55 DEBUG(LOG_INFO, "unicasting packet to client ciaddr"); 56 ciaddr = payload->ciaddr; 57 chaddr = payload->chaddr; 58 } else if (ntohs(payload->flags) & BROADCAST_FLAG) { 59 DEBUG(LOG_INFO, "broadcasting packet to client (requested)"); 60 ciaddr = INADDR_BROADCAST; 61 chaddr = MAC_BCAST_ADDR; 62 } else { 63 DEBUG(LOG_INFO, "unicasting packet to client yiaddr"); 64 ciaddr = payload->yiaddr; 65 chaddr = payload->chaddr; 66 } 67 return raw_packet(payload, server_config.server, SERVER_PORT, 68 ciaddr, CLIENT_PORT, chaddr, server_config.ifindex); 69} 70 71 72/* send a dhcp packet, if force broadcast is set, the packet will be broadcast to the client */ 73static int send_packet(struct dhcpMessage *payload, int force_broadcast) 74{ 75 int ret; 76 77 if (payload->giaddr) 78 ret = send_packet_to_relay(payload); 79 else ret = send_packet_to_client(payload, force_broadcast); 80 return ret; 81} 82 83 84static void init_packet(struct dhcpMessage *packet, struct dhcpMessage *oldpacket, char type) 85{ 86 init_header(packet, type); 87 packet->xid = oldpacket->xid; 88 memcpy(packet->chaddr, oldpacket->chaddr, 16); 89 packet->flags = oldpacket->flags; 90 packet->giaddr = oldpacket->giaddr; 91 packet->ciaddr = oldpacket->ciaddr; 92 add_simple_option(packet->options, DHCP_SERVER_ID, server_config.server); 93} 94 95 96/* add in the bootp options */ 97static void add_bootp_options(struct dhcpMessage *packet) 98{ 99 packet->siaddr = server_config.siaddr; 100 if (server_config.sname) 101 strncpy((char *)packet->sname, server_config.sname, sizeof(packet->sname) - 1); 102 if (server_config.boot_file) 103 strncpy((char *)packet->file, server_config.boot_file, sizeof(packet->file) - 1); 104} 105 106 107/* send a DHCP OFFER to a DHCP DISCOVER */ 108int sendOffer(struct dhcpMessage *oldpacket) 109{ 110 struct dhcpMessage packet; 111 struct dhcpOfferedAddr *lease = NULL; 112 u_int32_t req_align, lease_time_align = server_config.lease; 113 unsigned char *req, *lease_time; 114 struct option_set *curr; 115 struct in_addr addr; 116 117 init_packet(&packet, oldpacket, DHCPOFFER); 118 119 /* ADDME: if static, short circuit */ 120 /* the client is in our lease/offered table */ 121 if ((lease = find_lease_by_chaddr(oldpacket->chaddr))) { 122 if (!lease_expired(lease)) 123 lease_time_align = lease->expires - time(0); 124 packet.yiaddr = lease->yiaddr; 125 126 /* Or the client has a requested ip */ 127 } else if ((req = get_option(oldpacket, DHCP_REQUESTED_IP)) && 128 129 /* Don't look here (ugly hackish thing to do) */ 130 memcpy(&req_align, req, 4) && 131 132 /* and the ip is in the lease range */ 133 ntohl(req_align) >= ntohl(server_config.start) && 134 ntohl(req_align) <= ntohl(server_config.end) && 135 136 /* and its not already taken/offered */ /* ADDME: check that its not a static lease */ 137 ((!(lease = find_lease_by_yiaddr(req_align)) || 138 139 /* or its taken, but expired */ /* ADDME: or maybe in here */ 140 lease_expired(lease)))) { 141 packet.yiaddr = req_align; 142 143 /* otherwise, find a free IP */ /*ADDME: is it a static lease? */ 144 } else { 145 packet.yiaddr = find_address(0); 146 147 /* try for an expired lease */ 148 if (!packet.yiaddr) packet.yiaddr = find_address(1); 149 } 150 151 if(!packet.yiaddr) { 152 LOG(LOG_WARNING, "no IP addresses to give -- OFFER abandoned"); 153 return -1; 154 } 155 156 if (!add_lease(packet.chaddr, packet.yiaddr, server_config.offer_time)) { 157 LOG(LOG_WARNING, "lease pool is full -- OFFER abandoned"); 158 return -1; 159 } 160 161 if ((lease_time = get_option(oldpacket, DHCP_LEASE_TIME))) { 162 memcpy(&lease_time_align, lease_time, 4); 163 lease_time_align = ntohl(lease_time_align); 164 if (lease_time_align > server_config.lease) 165 lease_time_align = server_config.lease; 166 } 167 168 /* Make sure we aren't just using the lease time from the previous offer */ 169 if (lease_time_align < server_config.min_lease) 170 lease_time_align = server_config.lease; 171 /* ADDME: end of short circuit */ 172 add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_align)); 173 174 curr = server_config.options; 175 while (curr) { 176 if (curr->data[OPT_CODE] != DHCP_LEASE_TIME) 177 add_option_string(packet.options, curr->data); 178 curr = curr->next; 179 } 180 181 add_bootp_options(&packet); 182 183 addr.s_addr = packet.yiaddr; 184 LOG(LOG_INFO, "sending OFFER of %s", inet_ntoa(addr)); 185 return send_packet(&packet, 0); 186} 187 188 189int sendNAK(struct dhcpMessage *oldpacket) 190{ 191 struct dhcpMessage packet; 192 193 init_packet(&packet, oldpacket, DHCPNAK); 194 195 DEBUG(LOG_INFO, "sending NAK"); 196 return send_packet(&packet, 1); 197} 198 199 200int sendACK(struct dhcpMessage *oldpacket, u_int32_t yiaddr) 201{ 202 struct dhcpMessage packet; 203 struct option_set *curr; 204 unsigned char *lease_time; 205 u_int32_t lease_time_align = server_config.lease; 206 struct in_addr addr; 207 208 init_packet(&packet, oldpacket, DHCPACK); 209 packet.yiaddr = yiaddr; 210 211 if ((lease_time = get_option(oldpacket, DHCP_LEASE_TIME))) { 212 memcpy(&lease_time_align, lease_time, 4); 213 lease_time_align = ntohl(lease_time_align); 214 if (lease_time_align > server_config.lease) 215 lease_time_align = server_config.lease; 216 else if (lease_time_align < server_config.min_lease) 217 lease_time_align = server_config.lease; 218 } 219 220 add_simple_option(packet.options, DHCP_LEASE_TIME, htonl(lease_time_align)); 221 222 curr = server_config.options; 223 while (curr) { 224 if (curr->data[OPT_CODE] != DHCP_LEASE_TIME) 225 add_option_string(packet.options, curr->data); 226 curr = curr->next; 227 } 228 229 add_bootp_options(&packet); 230 231 addr.s_addr = packet.yiaddr; 232 LOG(LOG_INFO, "sending ACK to %s", inet_ntoa(addr)); 233 234 if (send_packet(&packet, 0) < 0) 235 return -1; 236 237 add_lease(packet.chaddr, packet.yiaddr, lease_time_align); 238 239 return 0; 240} 241 242 243int send_inform(struct dhcpMessage *oldpacket) 244{ 245 struct dhcpMessage packet; 246 struct option_set *curr; 247 248 init_packet(&packet, oldpacket, DHCPACK); 249 250 curr = server_config.options; 251 while (curr) { 252 if (curr->data[OPT_CODE] != DHCP_LEASE_TIME) 253 add_option_string(packet.options, curr->data); 254 curr = curr->next; 255 } 256 257 add_bootp_options(&packet); 258 259 return send_packet(&packet, 0); 260} 261