1 /*
2  * ip-cooked.c
3  *
4  * Copyright (c) 2001 Dug Song <dugsong@monkey.org>
5  *
6  * $Id: ip-cooked.c 547 2005-01-25 21:30:40Z dugsong $
7  */
8 
9 #ifdef _WIN32
10 #include "dnet_winconfig.h"
11 #else
12 #include "config.h"
13 #endif
14 
15 #ifndef _WIN32
16 #include <netinet/in.h>
17 #include <unistd.h>
18 #endif
19 #include <errno.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 
24 #include "dnet.h"
25 #include "queue.h"
26 
27 struct ip_intf {
28 	eth_t			*eth;
29 	char			 name[INTF_NAME_LEN];
30 	struct addr		 ha;
31 	struct addr		 pa;
32 	int			 mtu;
33 	LIST_ENTRY(ip_intf)	 next;
34 };
35 
36 struct ip_handle {
37 	arp_t			*arp;
38 	intf_t			*intf;
39 	route_t			*route;
40 	int			 fd;
41 	struct sockaddr_in	 sin;
42 
43 	LIST_HEAD(, ip_intf)	 ip_intf_list;
44 };
45 
46 static int
_add_ip_intf(const struct intf_entry * entry,void * arg)47 _add_ip_intf(const struct intf_entry *entry, void *arg)
48 {
49 	ip_t *ip = (ip_t *)arg;
50 	struct ip_intf *ipi;
51 
52 	if (entry->intf_type == INTF_TYPE_ETH &&
53 	    (entry->intf_flags & INTF_FLAG_UP) != 0 &&
54 	    entry->intf_mtu >= ETH_LEN_MIN &&
55 	    entry->intf_addr.addr_type == ADDR_TYPE_IP &&
56 	    entry->intf_link_addr.addr_type == ADDR_TYPE_ETH) {
57 
58 		if ((ipi = calloc(1, sizeof(*ipi))) == NULL)
59 			return (-1);
60 
61 		strlcpy(ipi->name, entry->intf_name, sizeof(ipi->name));
62 		memcpy(&ipi->ha, &entry->intf_link_addr, sizeof(ipi->ha));
63 		memcpy(&ipi->pa, &entry->intf_addr, sizeof(ipi->pa));
64 		ipi->mtu = entry->intf_mtu;
65 
66 		LIST_INSERT_HEAD(&ip->ip_intf_list, ipi, next);
67 	}
68 	return (0);
69 }
70 
71 ip_t *
ip_open(void)72 ip_open(void)
73 {
74 	ip_t *ip;
75 
76 	if ((ip = calloc(1, sizeof(*ip))) != NULL) {
77 		ip->fd = -1;
78 
79 		if ((ip->arp = arp_open()) == NULL ||
80 		    (ip->intf = intf_open()) == NULL ||
81 		    (ip->route = route_open()) == NULL)
82 			return (ip_close(ip));
83 
84 		if ((ip->fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
85 			return (ip_close(ip));
86 
87 		memset(&ip->sin, 0, sizeof(ip->sin));
88 		ip->sin.sin_family = AF_INET;
89 		ip->sin.sin_port = htons(666);
90 
91 		LIST_INIT(&ip->ip_intf_list);
92 
93 		if (intf_loop(ip->intf, _add_ip_intf, ip) != 0)
94 			return (ip_close(ip));
95 	}
96 	return (ip);
97 }
98 
99 static struct ip_intf *
_lookup_ip_intf(ip_t * ip,ip_addr_t dst)100 _lookup_ip_intf(ip_t *ip, ip_addr_t dst)
101 {
102 	struct ip_intf *ipi;
103 	int n;
104 
105 	ip->sin.sin_addr.s_addr = dst;
106 	n = sizeof(ip->sin);
107 
108 	if (connect(ip->fd, (struct sockaddr *)&ip->sin, n) < 0)
109 		return (NULL);
110 
111 	if (getsockname(ip->fd, (struct sockaddr *)&ip->sin, &n) < 0)
112 		return (NULL);
113 
114 	LIST_FOREACH(ipi, &ip->ip_intf_list, next) {
115 		if (ipi->pa.addr_ip == ip->sin.sin_addr.s_addr) {
116 			if (ipi->eth == NULL) {
117 				if ((ipi->eth = eth_open(ipi->name)) == NULL)
118 					return (NULL);
119 			}
120 			if (ipi != LIST_FIRST(&ip->ip_intf_list)) {
121 				LIST_REMOVE(ipi, next);
122 				LIST_INSERT_HEAD(&ip->ip_intf_list, ipi, next);
123 			}
124 			return (ipi);
125 		}
126 	}
127 	return (NULL);
128 }
129 
130 static void
_request_arp(struct ip_intf * ipi,struct addr * dst)131 _request_arp(struct ip_intf *ipi, struct addr *dst)
132 {
133 	u_char frame[ETH_HDR_LEN + ARP_HDR_LEN + ARP_ETHIP_LEN];
134 
135 	eth_pack_hdr(frame, ETH_ADDR_BROADCAST, ipi->ha.addr_eth,
136 	    ETH_TYPE_ARP);
137 	arp_pack_hdr_ethip(frame + ETH_HDR_LEN, ARP_OP_REQUEST,
138 	    ipi->ha.addr_eth, ipi->pa.addr_ip, ETH_ADDR_BROADCAST,
139 	    dst->addr_ip);
140 
141 	eth_send(ipi->eth, frame, sizeof(frame));
142 }
143 
144 ssize_t
ip_send(ip_t * ip,const void * buf,size_t len)145 ip_send(ip_t *ip, const void *buf, size_t len)
146 {
147 	struct ip_hdr *iph;
148 	struct ip_intf *ipi;
149 	struct arp_entry arpent;
150 	struct route_entry rtent;
151 	u_char frame[ETH_LEN_MAX];
152 	int i, usec;
153 
154 	iph = (struct ip_hdr *)buf;
155 
156 	if ((ipi = _lookup_ip_intf(ip, iph->ip_dst)) == NULL) {
157 		errno = EHOSTUNREACH;
158 		return (-1);
159 	}
160 	arpent.arp_pa.addr_type = ADDR_TYPE_IP;
161 	arpent.arp_pa.addr_bits = IP_ADDR_BITS;
162 	arpent.arp_pa.addr_ip = iph->ip_dst;
163 	memcpy(&rtent.route_dst, &arpent.arp_pa, sizeof(rtent.route_dst));
164 
165 	for (i = 0, usec = 10; i < 3; i++, usec *= 100) {
166 		if (arp_get(ip->arp, &arpent) == 0)
167 			break;
168 
169 		if (route_get(ip->route, &rtent) == 0 &&
170 		    rtent.route_gw.addr_ip != ipi->pa.addr_ip) {
171 			memcpy(&arpent.arp_pa, &rtent.route_gw,
172 			    sizeof(arpent.arp_pa));
173 			if (arp_get(ip->arp, &arpent) == 0)
174 				break;
175 		}
176 		_request_arp(ipi, &arpent.arp_pa);
177 
178 		usleep(usec);
179 	}
180 	if (i == 3)
181 		memset(&arpent.arp_ha.addr_eth, 0xff, ETH_ADDR_LEN);
182 
183 	eth_pack_hdr(frame, arpent.arp_ha.addr_eth,
184 	    ipi->ha.addr_eth, ETH_TYPE_IP);
185 
186 	if (len > ipi->mtu) {
187 		u_char *p, *start, *end, *ip_data;
188 		int ip_hl, fraglen;
189 
190 		ip_hl = iph->ip_hl << 2;
191 		fraglen = ipi->mtu - ip_hl;
192 
193 		iph = (struct ip_hdr *)(frame + ETH_HDR_LEN);
194 		memcpy(iph, buf, ip_hl);
195 		ip_data = (u_char *)iph + ip_hl;
196 
197 		start = (u_char *)buf + ip_hl;
198 		end = (u_char *)buf + len;
199 
200 		for (p = start; p < end; ) {
201 			memcpy(ip_data, p, fraglen);
202 
203 			iph->ip_len = htons(ip_hl + fraglen);
204 			iph->ip_off = htons(((p + fraglen < end) ? IP_MF : 0) |
205 			    ((p - start) >> 3));
206 
207 			ip_checksum(iph, ip_hl + fraglen);
208 
209 			i = ETH_HDR_LEN + ip_hl + fraglen;
210 			if (eth_send(ipi->eth, frame, i) != i)
211 				return (-1);
212 			p += fraglen;
213 			if (end - p < fraglen)
214 				fraglen = end - p;
215 		}
216 		return (len);
217 	}
218 	memcpy(frame + ETH_HDR_LEN, buf, len);
219 	i = ETH_HDR_LEN + len;
220 	if (eth_send(ipi->eth, frame, i) != i)
221 		return (-1);
222 
223 	return (len);
224 }
225 
226 ip_t *
ip_close(ip_t * ip)227 ip_close(ip_t *ip)
228 {
229 	struct ip_intf *ipi, *nxt;
230 
231 	if (ip != NULL) {
232 		for (ipi = LIST_FIRST(&ip->ip_intf_list);
233 		    ipi != LIST_END(&ip->ip_intf_list); ipi = nxt) {
234 			nxt = LIST_NEXT(ipi, next);
235 			if (ipi->eth != NULL)
236 				eth_close(ipi->eth);
237 			free(ipi);
238 		}
239 		if (ip->fd >= 0)
240 			close(ip->fd);
241 		if (ip->route != NULL)
242 			route_close(ip->route);
243 		if (ip->intf != NULL)
244 			intf_close(ip->intf);
245 		if (ip->arp != NULL)
246 			arp_close(ip->arp);
247 		free(ip);
248 	}
249 	return (NULL);
250 }
251