/* * ip-cooked.c * * Copyright (c) 2001 Dug Song * * $Id: ip-cooked.c 547 2005-01-25 21:30:40Z dugsong $ */ #ifdef _WIN32 #include "dnet_winconfig.h" #else #include "config.h" #endif #ifndef _WIN32 #include #include #endif #include #include #include #include #include "dnet.h" #include "queue.h" struct ip_intf { eth_t *eth; char name[INTF_NAME_LEN]; struct addr ha; struct addr pa; int mtu; LIST_ENTRY(ip_intf) next; }; struct ip_handle { arp_t *arp; intf_t *intf; route_t *route; int fd; struct sockaddr_in sin; LIST_HEAD(, ip_intf) ip_intf_list; }; static int _add_ip_intf(const struct intf_entry *entry, void *arg) { ip_t *ip = (ip_t *)arg; struct ip_intf *ipi; if (entry->intf_type == INTF_TYPE_ETH && (entry->intf_flags & INTF_FLAG_UP) != 0 && entry->intf_mtu >= ETH_LEN_MIN && entry->intf_addr.addr_type == ADDR_TYPE_IP && entry->intf_link_addr.addr_type == ADDR_TYPE_ETH) { if ((ipi = calloc(1, sizeof(*ipi))) == NULL) return (-1); strlcpy(ipi->name, entry->intf_name, sizeof(ipi->name)); memcpy(&ipi->ha, &entry->intf_link_addr, sizeof(ipi->ha)); memcpy(&ipi->pa, &entry->intf_addr, sizeof(ipi->pa)); ipi->mtu = entry->intf_mtu; LIST_INSERT_HEAD(&ip->ip_intf_list, ipi, next); } return (0); } ip_t * ip_open(void) { ip_t *ip; if ((ip = calloc(1, sizeof(*ip))) != NULL) { ip->fd = -1; if ((ip->arp = arp_open()) == NULL || (ip->intf = intf_open()) == NULL || (ip->route = route_open()) == NULL) return (ip_close(ip)); if ((ip->fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) return (ip_close(ip)); memset(&ip->sin, 0, sizeof(ip->sin)); ip->sin.sin_family = AF_INET; ip->sin.sin_port = htons(666); LIST_INIT(&ip->ip_intf_list); if (intf_loop(ip->intf, _add_ip_intf, ip) != 0) return (ip_close(ip)); } return (ip); } static struct ip_intf * _lookup_ip_intf(ip_t *ip, ip_addr_t dst) { struct ip_intf *ipi; int n; ip->sin.sin_addr.s_addr = dst; n = sizeof(ip->sin); if (connect(ip->fd, (struct sockaddr *)&ip->sin, n) < 0) return (NULL); if (getsockname(ip->fd, (struct sockaddr *)&ip->sin, &n) < 0) return (NULL); LIST_FOREACH(ipi, &ip->ip_intf_list, next) { if (ipi->pa.addr_ip == ip->sin.sin_addr.s_addr) { if (ipi->eth == NULL) { if ((ipi->eth = eth_open(ipi->name)) == NULL) return (NULL); } if (ipi != LIST_FIRST(&ip->ip_intf_list)) { LIST_REMOVE(ipi, next); LIST_INSERT_HEAD(&ip->ip_intf_list, ipi, next); } return (ipi); } } return (NULL); } static void _request_arp(struct ip_intf *ipi, struct addr *dst) { u_char frame[ETH_HDR_LEN + ARP_HDR_LEN + ARP_ETHIP_LEN]; eth_pack_hdr(frame, ETH_ADDR_BROADCAST, ipi->ha.addr_eth, ETH_TYPE_ARP); arp_pack_hdr_ethip(frame + ETH_HDR_LEN, ARP_OP_REQUEST, ipi->ha.addr_eth, ipi->pa.addr_ip, ETH_ADDR_BROADCAST, dst->addr_ip); eth_send(ipi->eth, frame, sizeof(frame)); } ssize_t ip_send(ip_t *ip, const void *buf, size_t len) { struct ip_hdr *iph; struct ip_intf *ipi; struct arp_entry arpent; struct route_entry rtent; u_char frame[ETH_LEN_MAX]; int i, usec; iph = (struct ip_hdr *)buf; if ((ipi = _lookup_ip_intf(ip, iph->ip_dst)) == NULL) { errno = EHOSTUNREACH; return (-1); } arpent.arp_pa.addr_type = ADDR_TYPE_IP; arpent.arp_pa.addr_bits = IP_ADDR_BITS; arpent.arp_pa.addr_ip = iph->ip_dst; memcpy(&rtent.route_dst, &arpent.arp_pa, sizeof(rtent.route_dst)); for (i = 0, usec = 10; i < 3; i++, usec *= 100) { if (arp_get(ip->arp, &arpent) == 0) break; if (route_get(ip->route, &rtent) == 0 && rtent.route_gw.addr_ip != ipi->pa.addr_ip) { memcpy(&arpent.arp_pa, &rtent.route_gw, sizeof(arpent.arp_pa)); if (arp_get(ip->arp, &arpent) == 0) break; } _request_arp(ipi, &arpent.arp_pa); usleep(usec); } if (i == 3) memset(&arpent.arp_ha.addr_eth, 0xff, ETH_ADDR_LEN); eth_pack_hdr(frame, arpent.arp_ha.addr_eth, ipi->ha.addr_eth, ETH_TYPE_IP); if (len > ipi->mtu) { u_char *p, *start, *end, *ip_data; int ip_hl, fraglen; ip_hl = iph->ip_hl << 2; fraglen = ipi->mtu - ip_hl; iph = (struct ip_hdr *)(frame + ETH_HDR_LEN); memcpy(iph, buf, ip_hl); ip_data = (u_char *)iph + ip_hl; start = (u_char *)buf + ip_hl; end = (u_char *)buf + len; for (p = start; p < end; ) { memcpy(ip_data, p, fraglen); iph->ip_len = htons(ip_hl + fraglen); iph->ip_off = htons(((p + fraglen < end) ? IP_MF : 0) | ((p - start) >> 3)); ip_checksum(iph, ip_hl + fraglen); i = ETH_HDR_LEN + ip_hl + fraglen; if (eth_send(ipi->eth, frame, i) != i) return (-1); p += fraglen; if (end - p < fraglen) fraglen = end - p; } return (len); } memcpy(frame + ETH_HDR_LEN, buf, len); i = ETH_HDR_LEN + len; if (eth_send(ipi->eth, frame, i) != i) return (-1); return (len); } ip_t * ip_close(ip_t *ip) { struct ip_intf *ipi, *nxt; if (ip != NULL) { for (ipi = LIST_FIRST(&ip->ip_intf_list); ipi != LIST_END(&ip->ip_intf_list); ipi = nxt) { nxt = LIST_NEXT(ipi, next); if (ipi->eth != NULL) eth_close(ipi->eth); free(ipi); } if (ip->fd >= 0) close(ip->fd); if (ip->route != NULL) route_close(ip->route); if (ip->intf != NULL) intf_close(ip->intf); if (ip->arp != NULL) arp_close(ip->arp); free(ip); } return (NULL); }