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