char *dhcp_get_hostname(uint8_t *p, const uint8_t *z) { journal_ftrace(__func__); struct dhcp_opt_hdr *h; char *dst, *src; uint8_t len; h = dhcp_get_option(p, z, DHCP_OPT_HOSTNAME); if (h == NULL) return NULL; len = h->length; h++; src = (char *)((uint8_t *)h); dst = calloc(1, len + 1); if (dst != NULL) { strncpy(dst, src, len); dst[len] = '\0'; return dst; } return NULL; }
char *dhcp_get_fqdn(uint8_t *p, const uint8_t *z) { journal_ftrace(__func__); struct dhcp_opt_hdr *h; char *dst, *src; uint8_t len; h = dhcp_get_option(p, z, DHCP_OPT_FQDN); if (h == NULL || h->length <= DHCP_OPT_FQDN_FLAGS_LENGTH) return NULL; len = h->length - DHCP_OPT_FQDN_FLAGS_LENGTH; h++; src = (char *)((uint8_t *)h + DHCP_OPT_FQDN_FLAGS_LENGTH); dst = calloc(1, len + 1); if (dst != NULL) { strncpy(dst, src, len); dst[len] = '\0'; return dst; } return NULL; }
uint8_t dhcp_get_msgtype(uint8_t *p, const uint8_t *z) { journal_ftrace(__func__); struct dhcp_opt_hdr *h; h = dhcp_get_option(p, z, DHCP_OPT_MSGTYPE); if (h == NULL || h->length != 1) return 0; return *(uint8_t *)++h; }
static unsigned dhcp_number(const unsigned char *px, unsigned length, unsigned tag) { unsigned i; unsigned result; const unsigned char *option; unsigned option_length; dhcp_get_option(px, length, tag, &option, &option_length); if (option_length == 0) return 0xFFFFFFFF; result = 0; for (i=0; i<option_length; i++) result = result * 256 + option[i]; return result; }
static uint8_t check_packet_type(struct dhcp_packet *packet) { uint8_t *type; if (packet->hlen != ETH_ALEN) return 0; if (packet->op != BOOTREQUEST) return 0; type = dhcp_get_option(packet, DHCP_MESSAGE_TYPE); if (!type) return 0; if (*type < DHCP_MINTYPE) return 0; if (*type > DHCP_MAXTYPE) return 0; return *type; }
static gboolean listener_event(GIOChannel *channel, GIOCondition condition, gpointer user_data) { GDHCPServer *dhcp_server = user_data; struct dhcp_packet packet; struct dhcp_lease *lease; uint32_t requested_nip = 0; uint8_t type, *server_id_option, *request_ip_option, *host_name; int re; GDHCPOptionType option_type; char *option_value; if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { dhcp_server->listener_watch = 0; return FALSE; } re = dhcp_recv_l3_packet(&packet, dhcp_server->listener_sockfd); if (re < 0) return TRUE; type = check_packet_type(&packet); if (type == 0) return TRUE; server_id_option = dhcp_get_option(&packet, DHCP_SERVER_ID); if (server_id_option) { uint32_t server_nid = get_be32(server_id_option); if (server_nid != dhcp_server->server_nip) return TRUE; } request_ip_option = dhcp_get_option(&packet, DHCP_REQUESTED_IP); if (request_ip_option) requested_nip = get_be32(request_ip_option); lease = find_lease_by_mac(dhcp_server, packet.chaddr); switch (type) { case DHCPDISCOVER: debug(dhcp_server, "Received DISCOVER"); send_offer(dhcp_server, &packet, lease, requested_nip); break; case DHCPREQUEST: debug(dhcp_server, "Received REQUEST NIP %d", requested_nip); if (requested_nip == 0) { requested_nip = packet.ciaddr; if (requested_nip == 0) break; } if (lease && requested_nip == lease->lease_nip) { debug(dhcp_server, "Sending ACK"); host_name = dhcp_get_option(&packet, DHCP_HOST_NAME); option_type = dhcp_get_code_type(DHCP_HOST_NAME); option_value = malloc_option_value_string(host_name, option_type); send_ACK(dhcp_server, &packet, lease->lease_nip); if (dhcp_server->save_ack_lease_func) dhcp_server->save_ack_lease_func( option_value, lease->lease_mac, lease->lease_nip); g_free(option_value); break; } if (server_id_option || !lease) { debug(dhcp_server, "Sending NAK"); send_NAK(dhcp_server, &packet); } break; case DHCPDECLINE: debug(dhcp_server, "Received DECLINE"); if (!server_id_option) break; if (!request_ip_option) break; if (!lease) break; if (requested_nip == lease->lease_nip) remove_lease(dhcp_server, lease); break; case DHCPRELEASE: debug(dhcp_server, "Received RELEASE"); if (!server_id_option) break; if (!lease) break; if (packet.ciaddr == lease->lease_nip) lease_set_expire(dhcp_server, lease, time(NULL)); break; case DHCPINFORM: debug(dhcp_server, "Received INFORM"); send_inform(dhcp_server, &packet); break; } return TRUE; }
int dhcp_option82_handle(struct sk_buff *skb, struct dhcp_packet *dhcp, dba_result_t *res) { struct ethhdr *ethhdr = NULL; struct iphdr *iph = NULL; struct udphdr *udph = NULL; unsigned char *tail = NULL; if (unlikely(!skb || !dhcp || !res)) { return -1; } if ((DHCP_CLIENT_REQUEST == dhcp->op) && (res->module_type & DHCP_OPTION82_KMOD)) { if (dba_option82_debug) { printk(KERN_DEBUG "skb->dev->name : %s\n", skb->dev->name); printPacketBuffer(skb->data, skb->len); printk(KERN_DEBUG "res->len : %d\n", res->len); printk(KERN_DEBUG "res->data : \n"); printPacketBuffer((unsigned char *)res->data, res->len); } /* 253 = 2^8 - 2(option code len) */ if (unlikely((res->len) > 255)) { log_error("dhcp option82 length %d: too large!\n", res->len); res->result |= DBA_ERROR; return -1; } /* enlarge skb, pointer dhcp may change, so must recalculate */ if (dba_enlarge_skb(skb, DBA_ALIGN4(res->len + 2))) { log_error("dhcp option82 expand skb failed!\n"); res->result |= DBA_ERROR; return -1; } /* skb may change, so recalculate pointer */ ethhdr = eth_hdr(skb); iph = (struct iphdr *)(ethhdr + 1); udph = (struct udphdr *)IPv4_NXT_HDR(iph); dhcp = (struct dhcp_packet *)(udph + 1); #if 0 tail = skb_tail_pointer(skb); if (0xff == *((unsigned char *)(tail-1))) { /* append option82 */ *(tail-1) = 82; /* option 82 code */ *tail = res->len; /* option 82 length */ memcpy(skb_put(skb, res->len+2)+1, res->data, res->len); tail = skb_tail_pointer(skb); *(tail-1) = 0xff; /* dhcp end option */ } else { /* append option82 */ *(tail) = 82; /* option 82 code */ *(tail+1) = res->len; /* option 82 length */ memcpy(skb_put(skb, res->len+2)+2, res->data, res->len); tail = skb_tail_pointer(skb); *(tail-1) = 0xff; /* dhcp end option */ } #else /* geti skb tail */ tail = skb_tail_pointer(skb); /* get dhcp end option(0xff) */ if (tail = dhcp_get_option(dhcp, 0xff, tail)) { skb_put(skb, res->len + 2); /* append option82 */ *(tail) = 82; /* option 82 code */ *(tail + 1) = res->len; /* option 82 length */ memcpy(tail + 2, res->data, res->len); #if 0 tail = skb_tail_pointer(skb); *(tail-1) = 0xff; /* dhcp end option */ #endif *(tail + 2 + res->len) = 0xff;/* dhcp end option added just behind option82 */ } else { res->result |= DBA_ERROR; log_error("dhcp option82 cannot find option 255!\n"); return -1; } #endif /* recalculate ip length Checksum*/ /* ethhdr = eth_hdr(skb); iph = (struct iphdr *)(ethhdr + 1); udph = (struct udphdr *)IPv4_NXT_HDR(iph); */ /* ip header length */ iph->tot_len += (res->len + 2); /* ip checksum */ iph->check = 0; iph->check = ip_fast_csum(iph, iph->ihl); /* recalculate udp Checksum length */ udph->len += (res->len + 2); udph->check = 0; udph->check = csum_tcpudp_magic(iph->saddr, iph->daddr, udph->len, IPPROTO_UDP, csum_partial(udph, udph->len, 0)); res->result |= DBA_HANDLED; } return 0; }
static gboolean listener_event(GIOChannel *channel, GIOCondition condition, gpointer user_data) { GDHCPServer *dhcp_server = user_data; struct dhcp_packet packet; struct dhcp_lease *lease; uint32_t requested_nip = 0; uint8_t type, *server_id_option, *request_ip_option; int re; if (condition & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { dhcp_server->listener_watch = 0; return FALSE; } re = dhcp_recv_l3_packet(&packet, dhcp_server->listener_sockfd); if (re < 0) return TRUE; type = check_packet_type(&packet); if (type == 0) return TRUE; server_id_option = dhcp_get_option(&packet, DHCP_SERVER_ID); if (server_id_option) { uint32_t server_nid = dhcp_get_unaligned( (uint32_t *) server_id_option); if (server_nid != dhcp_server->server_nip) return TRUE; } request_ip_option = dhcp_get_option(&packet, DHCP_REQUESTED_IP); if (request_ip_option) requested_nip = dhcp_get_unaligned( (uint32_t *) request_ip_option); lease = find_lease_by_mac(dhcp_server, packet.chaddr); switch (type) { case DHCPDISCOVER: debug(dhcp_server, "Received DISCOVER"); send_offer(dhcp_server, &packet, lease, requested_nip); break; case DHCPREQUEST: debug(dhcp_server, "Received REQUEST NIP %d", requested_nip); if (requested_nip == 0) { requested_nip = packet.ciaddr; if (requested_nip == 0) break; } if (lease && requested_nip == lease->lease_nip) { debug(dhcp_server, "Sending ACK"); send_ACK(dhcp_server, &packet, lease->lease_nip); break; } if (server_id_option || lease == NULL) { debug(dhcp_server, "Sending NAK"); send_NAK(dhcp_server, &packet); } break; case DHCPDECLINE: debug(dhcp_server, "Received DECLINE"); if (server_id_option == NULL) break; if (request_ip_option == NULL) break; if (lease == NULL) break; if (requested_nip == lease->lease_nip) remove_lease(dhcp_server, lease); break; case DHCPRELEASE: debug(dhcp_server, "Received RELEASE"); if (server_id_option == NULL) break; if (lease == NULL) break; if (packet.ciaddr == lease->lease_nip) lease_set_expire(dhcp_server, lease, time(NULL)); break; case DHCPINFORM: debug(dhcp_server, "Received INFORM"); send_inform(dhcp_server, &packet); break; } return TRUE; }
void process_dhcp(struct Seaper *seap, struct NetFrame *frame, const unsigned char *px, unsigned length) { unsigned offset; struct DHCP dhcp; memset(&dhcp, 0, sizeof(dhcp)); if (length < 200) { FRAMERR(frame, "dhcp: frame too short\n"); return; } dhcp.op = px[0]; dhcp.hardware_type = px[1]; dhcp.hardware_address_length = px[2]; dhcp.hops = px[3]; dhcp.transaction_id = ex32be(px+4); dhcp.seconds_elapsed = ex16be(px+8); dhcp.flags = ex16be(px+10); dhcp.ciaddr = ex32be(px+12); dhcp.yiaddr = ex32be(px+16); dhcp.siaddr = ex32be(px+20); dhcp.giaddr = ex32be(px+24); memcpy(dhcp.chaddr, px+28, 16); dhcp.chaddr[16] = '\0'; memcpy(dhcp.sname, px+28+16, 64); dhcp.sname[64] = '\0'; memcpy(dhcp.file, px+28+16+64, 128); dhcp.file[128] = '\0'; offset = 28+16+64+128; if (offset+4 > length) return; if (memcmp(px+offset, "\x63\x82\x53\x63", 4) != 0) return; offset += 4; /* Process special options */ dhcp.msg = dhcp_number(px, length, 53); switch (dhcp.msg) { case 8: /* inform */ /* Process vendor specific information */ { const unsigned char *spec; unsigned spec_length; const unsigned char *id; unsigned id_length; dhcp_get_option(px, length, 43, &spec, &spec_length); dhcp_get_option(px, length, 60, &id, &id_length); if (spec_length && id_length) { process_record(seap, "application", REC_PRINTABLE, id, id_length, "info", REC_PRINTABLE, spec, spec_length, 0); } } } process_dhcp_options(seap, frame, px, length, offset, &dhcp); if (dhcp.overload_filename) process_dhcp_options(seap, frame, px, 28+16+64+128, 28+16+64, &dhcp); if (dhcp.overload_servername) process_dhcp_options(seap, frame, px, 28+16+64, 28+16, &dhcp); SAMPLE("BOOTP", "type", REC_UNSIGNED, &dhcp.op, sizeof(dhcp.op)); switch (dhcp.op) { case 1: /*BOOTP request */ break; case 2: /*BOOTP reply*/ switch (dhcp.msg) { case 2: break; case 5: /*ack*/ break; case 6: /*DHCP NACK*/ { const unsigned char *dst_mac; unsigned src_ip; if (dhcp.hardware_address_length != 6) { FRAMERR(frame, "dhcp: expected hardware address length = 6, found length = %d\n", dhcp.hardware_address_length); break; } if (memcmp(dhcp.chaddr, "\0\0\0\0\0\0", 6) == 0) { FRAMERR(frame, "dhcp: expected hardware address, but found [00:00:00:00:00:00]\n"); break; } else dst_mac = &dhcp.chaddr[0]; if (dhcp.server_identifier) src_ip = dhcp.server_identifier; else if (dhcp.siaddr) src_ip = dhcp.siaddr; else src_ip = frame->src_ipv4; process_record(seap, "proto", REC_SZ, "DHCP", -1, "op", REC_SZ, "NACK", -1, "src.ip", REC_IPv4, &src_ip, sizeof(src_ip), "dst.mac", REC_MACADDR, dst_mac, 6, 0); } break; case 8: break; default: FRAMERR(frame, "dhcp: unknown dhcp msg type %d\n", dhcp.msg); break; } break; default: FRAMERR(frame, "dhcp: unknown bootp op code %d\n", dhcp.op); break; } }