source: trunk/minix/servers/inet/generic/ip_write.c@ 20

Last change on this file since 20 was 9, checked in by Mattia Monga, 14 years ago

Minix 3.1.2a

File size: 11.9 KB
RevLine 
[9]1/*
2ip_write.c
3
4Copyright 1995 Philip Homburg
5*/
6
7#include "inet.h"
8#include "buf.h"
9#include "event.h"
10#include "type.h"
11
12#include "arp.h"
13#include "assert.h"
14#include "clock.h"
15#include "eth.h"
16#include "icmp_lib.h"
17#include "io.h"
18#include "ip.h"
19#include "ip_int.h"
20#include "ipr.h"
21
22THIS_FILE
23
24FORWARD void error_reply ARGS(( ip_fd_t *fd, int error ));
25
26PUBLIC int ip_write (fd, count)
27int fd;
28size_t count;
29{
30 ip_fd_t *ip_fd;
31 acc_t *pack;
32 int r;
33
34 ip_fd= &ip_fd_table[fd];
35 if (count > IP_MAX_PACKSIZE)
36 {
37 error_reply (ip_fd, EPACKSIZE);
38 return NW_OK;
39 }
40 pack= (*ip_fd->if_get_userdata)(ip_fd->if_srfd, (size_t)0,
41 count, FALSE);
42 if (!pack)
43 return NW_OK;
44 r= ip_send(fd, pack, count);
45 assert(r != NW_WOULDBLOCK);
46
47 if (r == NW_OK)
48 error_reply (ip_fd, count);
49 else
50 error_reply (ip_fd, r);
51 return NW_OK;
52}
53
54PUBLIC int ip_send(fd, data, data_len)
55int fd;
56acc_t *data;
57size_t data_len;
58{
59 ip_port_t *ip_port;
60 ip_fd_t *ip_fd;
61 ip_hdr_t *ip_hdr, *tmp_hdr;
62 ipaddr_t dstaddr, nexthop, hostrep_dst, my_ipaddr, netmask;
63 u8_t *addrInBytes;
64 acc_t *tmp_pack, *tmp_pack1;
65 int hdr_len, hdr_opt_len, r;
66 int type, ttl;
67 size_t req_mtu;
68 ev_arg_t arg;
69
70 ip_fd= &ip_fd_table[fd];
71 ip_port= ip_fd->if_port;
72
73 if (!(ip_fd->if_flags & IFF_OPTSET))
74 {
75 bf_afree(data);
76 return EBADMODE;
77 }
78
79 if (!(ip_fd->if_port->ip_flags & IPF_IPADDRSET))
80 {
81 /* Interface is down. What kind of error do we want? For
82 * the moment, we return OK.
83 */
84 bf_afree(data);
85 return NW_OK;
86 }
87
88 data_len= bf_bufsize(data);
89
90 if (ip_fd->if_ipopt.nwio_flags & NWIO_RWDATONLY)
91 {
92 tmp_pack= bf_memreq(IP_MIN_HDR_SIZE);
93 tmp_pack->acc_next= data;
94 data= tmp_pack;
95 data_len += IP_MIN_HDR_SIZE;
96 }
97 if (data_len<IP_MIN_HDR_SIZE)
98 {
99 bf_afree(data);
100 return EPACKSIZE;
101 }
102
103 data= bf_packIffLess(data, IP_MIN_HDR_SIZE);
104 ip_hdr= (ip_hdr_t *)ptr2acc_data(data);
105 if (data->acc_linkC != 1 || data->acc_buffer->buf_linkC != 1)
106 {
107 tmp_pack= bf_memreq(IP_MIN_HDR_SIZE);
108 tmp_hdr= (ip_hdr_t *)ptr2acc_data(tmp_pack);
109 *tmp_hdr= *ip_hdr;
110 tmp_pack->acc_next= bf_cut(data, IP_MIN_HDR_SIZE,
111 data_len-IP_MIN_HDR_SIZE);
112 bf_afree(data);
113 ip_hdr= tmp_hdr;
114 data= tmp_pack;
115 assert (data->acc_length >= IP_MIN_HDR_SIZE);
116 }
117
118 if (ip_fd->if_ipopt.nwio_flags & NWIO_HDR_O_SPEC)
119 {
120 hdr_opt_len= ip_fd->if_ipopt.nwio_hdropt.iho_opt_siz;
121 if (hdr_opt_len)
122 {
123 tmp_pack= bf_cut(data, 0, IP_MIN_HDR_SIZE);
124 tmp_pack1= bf_cut (data, IP_MIN_HDR_SIZE,
125 data_len-IP_MIN_HDR_SIZE);
126 bf_afree(data);
127 data= bf_packIffLess(tmp_pack, IP_MIN_HDR_SIZE);
128 ip_hdr= (ip_hdr_t *)ptr2acc_data(data);
129 tmp_pack= bf_memreq (hdr_opt_len);
130 memcpy (ptr2acc_data(tmp_pack), ip_fd->if_ipopt.
131 nwio_hdropt.iho_data, hdr_opt_len);
132 data->acc_next= tmp_pack;
133 tmp_pack->acc_next= tmp_pack1;
134 hdr_len= IP_MIN_HDR_SIZE+hdr_opt_len;
135 }
136 else
137 hdr_len= IP_MIN_HDR_SIZE;
138 ip_hdr->ih_vers_ihl= hdr_len/4;
139 ip_hdr->ih_tos= ip_fd->if_ipopt.nwio_tos;
140 ip_hdr->ih_flags_fragoff= 0;
141 if (ip_fd->if_ipopt.nwio_df)
142 ip_hdr->ih_flags_fragoff |= HTONS(IH_DONT_FRAG);
143 ip_hdr->ih_ttl= ip_fd->if_ipopt.nwio_ttl;
144 ttl= ORTD_UNREACHABLE+1; /* Don't check TTL */
145 }
146 else
147 {
148 hdr_len= (ip_hdr->ih_vers_ihl & IH_IHL_MASK)*4;
149 r= NW_OK;
150 if (hdr_len<IP_MIN_HDR_SIZE)
151 r= EINVAL;
152 else if (hdr_len>data_len)
153 r= EPACKSIZE;
154 else if (!ip_hdr->ih_ttl)
155 r= EINVAL;
156 if (r != NW_OK)
157 {
158 bf_afree(data);
159 return r;
160 }
161
162 data= bf_packIffLess(data, hdr_len);
163 ip_hdr= (ip_hdr_t *)ptr2acc_data(data);
164 if (hdr_len != IP_MIN_HDR_SIZE)
165 {
166 r= ip_chk_hdropt((u8_t *)(ptr2acc_data(data) +
167 IP_MIN_HDR_SIZE),
168 hdr_len-IP_MIN_HDR_SIZE);
169 if (r != NW_OK)
170 {
171 bf_afree(data);
172 return r;
173 }
174 }
175 ttl= ip_hdr->ih_ttl;
176 }
177
178 ip_hdr->ih_vers_ihl= (ip_hdr->ih_vers_ihl & IH_IHL_MASK) |
179 (IP_VERSION << 4);
180 ip_hdr->ih_length= htons(data_len);
181 ip_hdr->ih_flags_fragoff &= ~HTONS(IH_FRAGOFF_MASK |
182 IH_FLAGS_UNUSED | IH_MORE_FRAGS);
183 if (ip_fd->if_ipopt.nwio_flags & NWIO_PROTOSPEC)
184 ip_hdr->ih_proto= ip_fd->if_ipopt.nwio_proto;
185 ip_hdr->ih_id= htons(ip_port->ip_frame_id++);
186 ip_hdr->ih_src= ip_fd->if_port->ip_ipaddr;
187 if (ip_fd->if_ipopt.nwio_flags & NWIO_REMSPEC)
188 ip_hdr->ih_dst= ip_fd->if_ipopt.nwio_rem;
189
190 netmask= ip_port->ip_subnetmask;
191 my_ipaddr= ip_port->ip_ipaddr;
192
193 dstaddr= ip_hdr->ih_dst;
194 hostrep_dst= ntohl(dstaddr);
195 r= 0;
196 if (hostrep_dst == (ipaddr_t)-1)
197 ; /* OK, local broadcast */
198 else if ((hostrep_dst & 0xe0000000l) == 0xe0000000l)
199 ; /* OK, Multicast */
200 else if ((hostrep_dst & 0xf0000000l) == 0xf0000000l)
201 r= EBADDEST; /* Bad class */
202 else if ((dstaddr ^ my_ipaddr) & netmask)
203 ; /* OK, remote destination */
204 else if (!(dstaddr & ~netmask) &&
205 (ip_port->ip_flags & IPF_SUBNET_BCAST))
206 {
207 r= EBADDEST; /* Zero host part */
208 }
209 if (r<0)
210 {
211 DIFBLOCK(1, r == EBADDEST,
212 printf("bad destination: ");
213 writeIpAddr(ip_hdr->ih_dst);
214 printf("\n"));
215 bf_afree(data);
216 return r;
217 }
218 ip_hdr_chksum(ip_hdr, hdr_len);
219
220 data= bf_packIffLess(data, IP_MIN_HDR_SIZE);
221 assert (data->acc_length >= IP_MIN_HDR_SIZE);
222 ip_hdr= (ip_hdr_t *)ptr2acc_data(data);
223
224 if (ip_hdr->ih_flags_fragoff & HTONS(IH_DONT_FRAG))
225 {
226 req_mtu= bf_bufsize(data);
227 if (req_mtu > ip_port->ip_mtu)
228 {
229 DBLOCK(1, printf(
230 "packet is larger than link MTU and DF is set\n"));
231 bf_afree(data);
232 return EPACKSIZE;
233 }
234 }
235 else
236 req_mtu= 0;
237
238 addrInBytes= (u8_t *)&dstaddr;
239
240 if ((addrInBytes[0] & 0xff) == 0x7f) /* local loopback */
241 {
242 assert (data->acc_linkC == 1);
243 dstaddr= ip_hdr->ih_dst; /* swap src and dst
244 * addresses */
245 ip_hdr->ih_dst= ip_hdr->ih_src;
246 ip_hdr->ih_src= dstaddr;
247 data->acc_ext_link= NULL;
248 if (ip_port->ip_loopb_head == NULL)
249 {
250 ip_port->ip_loopb_head= data;
251 arg.ev_ptr= ip_port;
252 ev_enqueue(&ip_port->ip_loopb_event,
253 ip_process_loopb, arg);
254 }
255 else
256 ip_port->ip_loopb_tail->acc_ext_link= data;
257 ip_port->ip_loopb_tail= data;
258
259 return NW_OK;
260 }
261
262 if ((dstaddr & HTONL(0xe0000000)) == HTONL(0xe0000000))
263 {
264 if (dstaddr == (ipaddr_t)-1)
265 {
266 r= (*ip_port->ip_dev_send)(ip_port, dstaddr, data,
267 IP_LT_BROADCAST);
268 return r;
269 }
270 if (ip_nettype(dstaddr) == IPNT_CLASS_D)
271 {
272 /* Multicast, what about multicast routing? */
273 r= (*ip_port->ip_dev_send)(ip_port, dstaddr, data,
274 IP_LT_MULTICAST);
275 return r;
276 }
277 }
278
279 if (dstaddr == my_ipaddr)
280 {
281 assert (data->acc_linkC == 1);
282
283 data->acc_ext_link= NULL;
284 if (ip_port->ip_loopb_head == NULL)
285 {
286 ip_port->ip_loopb_head= data;
287 arg.ev_ptr= ip_port;
288 ev_enqueue(&ip_port->ip_loopb_event,
289 ip_process_loopb, arg);
290 }
291 else
292 ip_port->ip_loopb_tail->acc_ext_link= data;
293 ip_port->ip_loopb_tail= data;
294
295 return NW_OK;
296 }
297
298 if (((dstaddr ^ my_ipaddr) & netmask) == 0)
299 {
300 type= ((dstaddr == (my_ipaddr | ~netmask) &&
301 (ip_port->ip_flags & IPF_SUBNET_BCAST)) ?
302 IP_LT_BROADCAST : IP_LT_NORMAL);
303
304 r= (*ip_port->ip_dev_send)(ip_port, dstaddr, data, type);
305 return r;
306 }
307
308 r= oroute_frag (ip_port - ip_port_table, dstaddr, ttl, req_mtu,
309 &nexthop);
310
311 if (r == NW_OK)
312 {
313 if (nexthop == ip_port->ip_ipaddr)
314 {
315 data->acc_ext_link= NULL;
316 if (ip_port->ip_loopb_head == NULL)
317 {
318 ip_port->ip_loopb_head= data;
319 arg.ev_ptr= ip_port;
320 ev_enqueue(&ip_port->ip_loopb_event,
321 ip_process_loopb, arg);
322 }
323 else
324 ip_port->ip_loopb_tail->acc_ext_link= data;
325 ip_port->ip_loopb_tail= data;
326 }
327 else
328 {
329 r= (*ip_port->ip_dev_send)(ip_port,
330 nexthop, data, IP_LT_NORMAL);
331 }
332 }
333 else
334 {
335 DBLOCK(0x10, printf("got error %d\n", r));
336 bf_afree(data);
337 }
338 return r;
339}
340
341PUBLIC void ip_hdr_chksum(ip_hdr, ip_hdr_len)
342ip_hdr_t *ip_hdr;
343int ip_hdr_len;
344{
345 ip_hdr->ih_hdr_chk= 0;
346 ip_hdr->ih_hdr_chk= ~oneC_sum (0, (u16_t *)ip_hdr, ip_hdr_len);
347}
348
349PUBLIC acc_t *ip_split_pack (ip_port, ref_last, mtu)
350ip_port_t *ip_port;
351acc_t **ref_last;
352int mtu;
353{
354 int pack_siz;
355 ip_hdr_t *first_hdr, *second_hdr;
356 int first_hdr_len, second_hdr_len;
357 int first_data_len, second_data_len;
358 int data_len, max_data_len, nfrags, new_first_data_len;
359 int first_opt_size, second_opt_size;
360 acc_t *first_pack, *second_pack, *tmp_pack;
361 u8_t *first_optptr, *second_optptr;
362 int i, optlen;
363
364 first_pack= *ref_last;
365 *ref_last= 0;
366 second_pack= 0;
367
368 first_pack= bf_align(first_pack, IP_MIN_HDR_SIZE, 4);
369 first_pack= bf_packIffLess(first_pack, IP_MIN_HDR_SIZE);
370 assert (first_pack->acc_length >= IP_MIN_HDR_SIZE);
371
372 first_hdr= (ip_hdr_t *)ptr2acc_data(first_pack);
373 first_hdr_len= (first_hdr->ih_vers_ihl & IH_IHL_MASK) * 4;
374 if (first_hdr_len>IP_MIN_HDR_SIZE)
375 {
376 first_pack= bf_packIffLess(first_pack, first_hdr_len);
377 first_hdr= (ip_hdr_t *)ptr2acc_data(first_pack);
378 }
379
380 pack_siz= bf_bufsize(first_pack);
381 assert(pack_siz > mtu);
382
383 assert (!(first_hdr->ih_flags_fragoff & HTONS(IH_DONT_FRAG)));
384
385 if (first_pack->acc_linkC != 1 ||
386 first_pack->acc_buffer->buf_linkC != 1)
387 {
388 /* Get a private copy of the IP header */
389 tmp_pack= bf_memreq(first_hdr_len);
390 memcpy(ptr2acc_data(tmp_pack), first_hdr, first_hdr_len);
391 first_pack= bf_delhead(first_pack, first_hdr_len);
392 tmp_pack->acc_next= first_pack;
393 first_pack= tmp_pack; tmp_pack= NULL;
394 first_hdr= (ip_hdr_t *)ptr2acc_data(first_pack);
395 }
396
397 data_len= ntohs(first_hdr->ih_length) - first_hdr_len;
398
399 /* Try to split the packet evenly. */
400 assert(mtu > first_hdr_len);
401 max_data_len= mtu-first_hdr_len;
402 nfrags= (data_len/max_data_len)+1;
403 new_first_data_len= data_len/nfrags;
404 if (new_first_data_len < 8)
405 {
406 /* Special case for extremely small MTUs */
407 new_first_data_len= 8;
408 }
409 new_first_data_len &= ~7; /* data goes in 8 byte chuncks */
410
411 assert(new_first_data_len >= 8);
412 assert(new_first_data_len+first_hdr_len <= mtu);
413
414 second_data_len= data_len-new_first_data_len;
415 second_pack= bf_cut(first_pack, first_hdr_len+
416 new_first_data_len, second_data_len);
417 tmp_pack= first_pack;
418 first_data_len= new_first_data_len;
419 first_pack= bf_cut (tmp_pack, 0, first_hdr_len+first_data_len);
420 bf_afree(tmp_pack);
421 tmp_pack= bf_memreq(first_hdr_len);
422 tmp_pack->acc_next= second_pack;
423 second_pack= tmp_pack;
424 second_hdr= (ip_hdr_t *)ptr2acc_data(second_pack);
425 *second_hdr= *first_hdr;
426 second_hdr->ih_flags_fragoff= htons(
427 ntohs(first_hdr->ih_flags_fragoff)+(first_data_len/8));
428
429 first_opt_size= first_hdr_len-IP_MIN_HDR_SIZE;
430 second_opt_size= 0;
431 if (first_opt_size)
432 {
433 first_pack= bf_packIffLess (first_pack,
434 first_hdr_len);
435 first_hdr= (ip_hdr_t *)ptr2acc_data(first_pack);
436 assert (first_pack->acc_length>=first_hdr_len);
437 first_optptr= (u8_t *)ptr2acc_data(first_pack)+
438 IP_MIN_HDR_SIZE;
439 second_optptr= (u8_t *)ptr2acc_data(
440 second_pack)+IP_MIN_HDR_SIZE;
441 i= 0;
442 while (i<first_opt_size)
443 {
444 switch (*first_optptr & IP_OPT_NUMBER)
445 {
446 case 0:
447 case 1:
448 optlen= 1;
449 break;
450 default:
451 optlen= first_optptr[1];
452 break;
453 }
454 assert (i + optlen <= first_opt_size);
455 i += optlen;
456 if (*first_optptr & IP_OPT_COPIED)
457 {
458 second_opt_size += optlen;
459 while (optlen--)
460 *second_optptr++=
461 *first_optptr++;
462 }
463 else
464 first_optptr += optlen;
465 }
466 while (second_opt_size & 3)
467 {
468 *second_optptr++= 0;
469 second_opt_size++;
470 }
471 }
472 second_hdr_len= IP_MIN_HDR_SIZE + second_opt_size;
473
474 second_hdr->ih_vers_ihl= (second_hdr->ih_vers_ihl & 0xf0)
475 + (second_hdr_len/4);
476 second_hdr->ih_length= htons(second_data_len+
477 second_hdr_len);
478 second_pack->acc_length= second_hdr_len;
479
480 assert(first_pack->acc_linkC == 1);
481 assert(first_pack->acc_buffer->buf_linkC == 1);
482
483 first_hdr->ih_flags_fragoff |= HTONS(IH_MORE_FRAGS);
484 first_hdr->ih_length= htons(first_data_len+
485 first_hdr_len);
486 assert (!(second_hdr->ih_flags_fragoff & HTONS(IH_DONT_FRAG)));
487
488 ip_hdr_chksum(first_hdr, first_hdr_len);
489 if (second_data_len+second_hdr_len <= mtu)
490 {
491 /* second_pack will not be split any further, so we have to
492 * calculate the header checksum.
493 */
494 ip_hdr_chksum(second_hdr, second_hdr_len);
495 }
496
497 *ref_last= second_pack;
498
499 return first_pack;
500}
501
502PRIVATE void error_reply (ip_fd, error)
503ip_fd_t *ip_fd;
504int error;
505{
506 if ((*ip_fd->if_get_userdata)(ip_fd->if_srfd, (size_t)error,
507 (size_t)0, FALSE))
508 {
509 ip_panic(( "can't error_reply" ));
510 }
511}
512
513/*
514 * $PchId: ip_write.c,v 1.22 2004/08/03 11:11:04 philip Exp $
515 */
Note: See TracBrowser for help on using the repository browser.