| #include "kvm/uip.h" |
| |
| #include <arpa/inet.h> |
| |
| #define EMPTY_ADDR "0.0.0.0" |
| |
| static inline bool uip_dhcp_is_discovery(struct uip_dhcp *dhcp) |
| { |
| return (dhcp->option[2] == UIP_DHCP_DISCOVER && |
| dhcp->option[1] == UIP_DHCP_TAG_MSG_TYPE_LEN && |
| dhcp->option[0] == UIP_DHCP_TAG_MSG_TYPE); |
| } |
| |
| static inline bool uip_dhcp_is_request(struct uip_dhcp *dhcp) |
| { |
| return (dhcp->option[2] == UIP_DHCP_REQUEST && |
| dhcp->option[1] == UIP_DHCP_TAG_MSG_TYPE_LEN && |
| dhcp->option[0] == UIP_DHCP_TAG_MSG_TYPE); |
| } |
| |
| bool uip_udp_is_dhcp(struct uip_udp *udp) |
| { |
| struct uip_dhcp *dhcp; |
| |
| if (ntohs(udp->sport) != UIP_DHCP_PORT_CLIENT || |
| ntohs(udp->dport) != UIP_DHCP_PORT_SERVER) |
| return false; |
| |
| dhcp = (struct uip_dhcp *)udp; |
| |
| if (ntohl(dhcp->magic_cookie) != UIP_DHCP_MAGIC_COOKIE) |
| return false; |
| |
| return true; |
| } |
| |
| int uip_dhcp_get_dns(struct uip_info *info) |
| { |
| char key[256], val[256]; |
| struct in_addr addr; |
| int ret = -1; |
| int n = 0; |
| FILE *fp; |
| u32 ip; |
| |
| fp = fopen("/etc/resolv.conf", "r"); |
| if (!fp) |
| return ret; |
| |
| while (!feof(fp)) { |
| if (fscanf(fp, "%s %s\n", key, val) != 2) |
| continue; |
| if (strncmp("domain", key, 6) == 0) |
| info->domain_name = strndup(val, UIP_DHCP_MAX_DOMAIN_NAME_LEN); |
| else if (strncmp("nameserver", key, 10) == 0) { |
| if (!inet_aton(val, &addr)) |
| continue; |
| ip = ntohl(addr.s_addr); |
| if (n < UIP_DHCP_MAX_DNS_SERVER_NR) |
| info->dns_ip[n++] = ip; |
| ret = 0; |
| } |
| } |
| |
| fclose(fp); |
| return ret; |
| } |
| |
| static int uip_dhcp_fill_option_name_and_server(struct uip_info *info, u8 *opt, int i) |
| { |
| u8 domain_name_len; |
| u32 *addr; |
| int n; |
| |
| if (info->domain_name) { |
| domain_name_len = strlen(info->domain_name); |
| opt[i++] = UIP_DHCP_TAG_DOMAIN_NAME; |
| opt[i++] = domain_name_len; |
| memcpy(&opt[i], info->domain_name, domain_name_len); |
| i += domain_name_len; |
| } |
| |
| for (n = 0; n < UIP_DHCP_MAX_DNS_SERVER_NR; n++) { |
| if (info->dns_ip[n] == 0) |
| continue; |
| opt[i++] = UIP_DHCP_TAG_DNS_SERVER; |
| opt[i++] = UIP_DHCP_TAG_DNS_SERVER_LEN; |
| addr = (u32 *)&opt[i]; |
| *addr = htonl(info->dns_ip[n]); |
| i += UIP_DHCP_TAG_DNS_SERVER_LEN; |
| } |
| |
| return i; |
| } |
| static int uip_dhcp_fill_option(struct uip_info *info, struct uip_dhcp *dhcp, int reply_msg_type) |
| { |
| int i = 0; |
| u32 *addr; |
| u8 *opt; |
| |
| opt = dhcp->option; |
| |
| opt[i++] = UIP_DHCP_TAG_MSG_TYPE; |
| opt[i++] = UIP_DHCP_TAG_MSG_TYPE_LEN; |
| opt[i++] = reply_msg_type; |
| |
| opt[i++] = UIP_DHCP_TAG_SERVER_ID; |
| opt[i++] = UIP_DHCP_TAG_SERVER_ID_LEN; |
| addr = (u32 *)&opt[i]; |
| *addr = htonl(info->host_ip); |
| i += UIP_DHCP_TAG_SERVER_ID_LEN; |
| |
| opt[i++] = UIP_DHCP_TAG_LEASE_TIME; |
| opt[i++] = UIP_DHCP_TAG_LEASE_TIME_LEN; |
| addr = (u32 *)&opt[i]; |
| *addr = htonl(UIP_DHCP_LEASE_TIME); |
| i += UIP_DHCP_TAG_LEASE_TIME_LEN; |
| |
| opt[i++] = UIP_DHCP_TAG_SUBMASK; |
| opt[i++] = UIP_DHCP_TAG_SUBMASK_LEN; |
| addr = (u32 *)&opt[i]; |
| *addr = htonl(info->guest_netmask); |
| i += UIP_DHCP_TAG_SUBMASK_LEN; |
| |
| opt[i++] = UIP_DHCP_TAG_ROUTER; |
| opt[i++] = UIP_DHCP_TAG_ROUTER_LEN; |
| addr = (u32 *)&opt[i]; |
| *addr = htonl(info->host_ip); |
| i += UIP_DHCP_TAG_ROUTER_LEN; |
| |
| opt[i++] = UIP_DHCP_TAG_ROOT; |
| opt[i++] = strlen(EMPTY_ADDR); |
| addr = (u32 *)&opt[i]; |
| strcpy((void *) addr, EMPTY_ADDR); |
| i += strlen(EMPTY_ADDR); |
| |
| i = uip_dhcp_fill_option_name_and_server(info, opt, i); |
| |
| opt[i++] = UIP_DHCP_TAG_END; |
| |
| return 0; |
| } |
| |
| static int uip_dhcp_make_pkg(struct uip_info *info, struct uip_udp_socket *sk, struct uip_buf *buf, u8 reply_msg_type) |
| { |
| struct uip_dhcp *dhcp; |
| |
| dhcp = (struct uip_dhcp *)buf->eth; |
| |
| dhcp->msg_type = 2; |
| dhcp->client_ip = 0; |
| dhcp->your_ip = htonl(info->guest_ip); |
| dhcp->server_ip = htonl(info->host_ip); |
| dhcp->agent_ip = 0; |
| |
| uip_dhcp_fill_option(info, dhcp, reply_msg_type); |
| |
| sk->sip = htonl(info->guest_ip); |
| sk->dip = htonl(info->host_ip); |
| sk->sport = htons(UIP_DHCP_PORT_CLIENT); |
| sk->dport = htons(UIP_DHCP_PORT_SERVER); |
| |
| return 0; |
| } |
| |
| int uip_tx_do_ipv4_udp_dhcp(struct uip_tx_arg *arg) |
| { |
| struct uip_udp_socket sk; |
| struct uip_dhcp *dhcp; |
| struct uip_info *info; |
| struct uip_buf *buf; |
| u8 reply_msg_type; |
| |
| dhcp = (struct uip_dhcp *)arg->eth; |
| |
| if (uip_dhcp_is_discovery(dhcp)) |
| reply_msg_type = UIP_DHCP_OFFER; |
| else if (uip_dhcp_is_request(dhcp)) |
| reply_msg_type = UIP_DHCP_ACK; |
| else |
| return -1; |
| |
| buf = uip_buf_clone(arg); |
| info = arg->info; |
| |
| /* |
| * Cook DHCP pkg |
| */ |
| uip_dhcp_make_pkg(info, &sk, buf, reply_msg_type); |
| |
| /* |
| * Cook UDP pkg |
| */ |
| uip_udp_make_pkg(info, &sk, buf, NULL, UIP_DHCP_MAX_PAYLOAD_LEN); |
| |
| /* |
| * Send data received from socket to guest |
| */ |
| uip_buf_set_used(info, buf); |
| |
| return 0; |
| } |
| |
| void uip_dhcp_exit(struct uip_info *info) |
| { |
| free(info->domain_name); |
| info->domain_name = NULL; |
| } |