VM: simplify slab allocator
[minix.git] / servers / inet / generic / ip_write.c
blobb363036e1db49da998cfdd228c35a7a7771e2d36
1 /*
2 ip_write.c
4 Copyright 1995 Philip Homburg
5 */
7 #include "inet.h"
8 #include "buf.h"
9 #include "event.h"
10 #include "type.h"
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"
22 THIS_FILE
24 static void error_reply ARGS(( ip_fd_t *fd, int error ));
26 int ip_write (fd, count)
27 int fd;
28 size_t count;
30 ip_fd_t *ip_fd;
31 acc_t *pack;
32 int r;
34 ip_fd= &ip_fd_table[fd];
35 if (count > IP_MAX_PACKSIZE)
37 error_reply (ip_fd, EPACKSIZE);
38 return NW_OK;
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);
47 if (r == NW_OK)
48 error_reply (ip_fd, count);
49 else
50 error_reply (ip_fd, r);
51 return NW_OK;
54 int ip_send(fd, data, data_len)
55 int fd;
56 acc_t *data;
57 size_t data_len;
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;
70 ip_fd= &ip_fd_table[fd];
71 ip_port= ip_fd->if_port;
73 if (!(ip_fd->if_flags & IFF_OPTSET))
75 bf_afree(data);
76 return EBADMODE;
79 if (!(ip_fd->if_port->ip_flags & IPF_IPADDRSET))
81 /* Interface is down. */
82 bf_afree(data);
83 return ENETDOWN;
86 data_len= bf_bufsize(data);
88 if (ip_fd->if_ipopt.nwio_flags & NWIO_RWDATONLY)
90 tmp_pack= bf_memreq(IP_MIN_HDR_SIZE);
91 tmp_pack->acc_next= data;
92 data= tmp_pack;
93 data_len += IP_MIN_HDR_SIZE;
95 if (data_len<IP_MIN_HDR_SIZE)
97 bf_afree(data);
98 return EPACKSIZE;
101 data= bf_packIffLess(data, IP_MIN_HDR_SIZE);
102 ip_hdr= (ip_hdr_t *)ptr2acc_data(data);
103 if (data->acc_linkC != 1 || data->acc_buffer->buf_linkC != 1)
105 tmp_pack= bf_memreq(IP_MIN_HDR_SIZE);
106 tmp_hdr= (ip_hdr_t *)ptr2acc_data(tmp_pack);
107 *tmp_hdr= *ip_hdr;
108 tmp_pack->acc_next= bf_cut(data, IP_MIN_HDR_SIZE,
109 data_len-IP_MIN_HDR_SIZE);
110 bf_afree(data);
111 ip_hdr= tmp_hdr;
112 data= tmp_pack;
113 assert (data->acc_length >= IP_MIN_HDR_SIZE);
116 if (ip_fd->if_ipopt.nwio_flags & NWIO_HDR_O_SPEC)
118 hdr_opt_len= ip_fd->if_ipopt.nwio_hdropt.iho_opt_siz;
119 if (hdr_opt_len)
121 tmp_pack= bf_cut(data, 0, IP_MIN_HDR_SIZE);
122 tmp_pack1= bf_cut (data, IP_MIN_HDR_SIZE,
123 data_len-IP_MIN_HDR_SIZE);
124 bf_afree(data);
125 data= bf_packIffLess(tmp_pack, IP_MIN_HDR_SIZE);
126 ip_hdr= (ip_hdr_t *)ptr2acc_data(data);
127 tmp_pack= bf_memreq (hdr_opt_len);
128 memcpy (ptr2acc_data(tmp_pack), ip_fd->if_ipopt.
129 nwio_hdropt.iho_data, hdr_opt_len);
130 data->acc_next= tmp_pack;
131 tmp_pack->acc_next= tmp_pack1;
132 hdr_len= IP_MIN_HDR_SIZE+hdr_opt_len;
134 else
135 hdr_len= IP_MIN_HDR_SIZE;
136 ip_hdr->ih_vers_ihl= hdr_len/4;
137 ip_hdr->ih_tos= ip_fd->if_ipopt.nwio_tos;
138 ip_hdr->ih_flags_fragoff= 0;
139 if (ip_fd->if_ipopt.nwio_df)
140 ip_hdr->ih_flags_fragoff |= HTONS(IH_DONT_FRAG);
141 ip_hdr->ih_ttl= ip_fd->if_ipopt.nwio_ttl;
142 ttl= ORTD_UNREACHABLE+1; /* Don't check TTL */
144 else
146 hdr_len= (ip_hdr->ih_vers_ihl & IH_IHL_MASK)*4;
147 r= NW_OK;
148 if (hdr_len<IP_MIN_HDR_SIZE)
149 r= EINVAL;
150 else if (hdr_len>data_len)
151 r= EPACKSIZE;
152 else if (!ip_hdr->ih_ttl)
153 r= EINVAL;
154 if (r != NW_OK)
156 bf_afree(data);
157 return r;
160 data= bf_packIffLess(data, hdr_len);
161 ip_hdr= (ip_hdr_t *)ptr2acc_data(data);
162 if (hdr_len != IP_MIN_HDR_SIZE)
164 r= ip_chk_hdropt((u8_t *)(ptr2acc_data(data) +
165 IP_MIN_HDR_SIZE),
166 hdr_len-IP_MIN_HDR_SIZE);
167 if (r != NW_OK)
169 bf_afree(data);
170 return r;
173 ttl= ip_hdr->ih_ttl;
176 ip_hdr->ih_vers_ihl= (ip_hdr->ih_vers_ihl & IH_IHL_MASK) |
177 (IP_VERSION << 4);
178 ip_hdr->ih_length= htons(data_len);
179 ip_hdr->ih_flags_fragoff &= ~HTONS(IH_FRAGOFF_MASK |
180 IH_FLAGS_UNUSED | IH_MORE_FRAGS);
181 if (ip_fd->if_ipopt.nwio_flags & NWIO_PROTOSPEC)
182 ip_hdr->ih_proto= ip_fd->if_ipopt.nwio_proto;
183 ip_hdr->ih_id= htons(ip_port->ip_frame_id++);
184 ip_hdr->ih_src= ip_fd->if_port->ip_ipaddr;
185 if (ip_fd->if_ipopt.nwio_flags & NWIO_REMSPEC)
186 ip_hdr->ih_dst= ip_fd->if_ipopt.nwio_rem;
188 netmask= ip_port->ip_subnetmask;
189 my_ipaddr= ip_port->ip_ipaddr;
191 dstaddr= ip_hdr->ih_dst;
192 hostrep_dst= ntohl(dstaddr);
193 r= 0;
194 if (hostrep_dst == (ipaddr_t)-1)
195 ; /* OK, local broadcast */
196 else if ((hostrep_dst & 0xe0000000l) == 0xe0000000l)
197 ; /* OK, Multicast */
198 else if ((hostrep_dst & 0xf0000000l) == 0xf0000000l)
199 r= EAFNOSUPPORT; /* Bad class */
200 else if ((dstaddr ^ my_ipaddr) & netmask)
201 ; /* OK, remote destination */
202 else if (!(dstaddr & ~netmask) &&
203 (ip_port->ip_flags & IPF_SUBNET_BCAST))
205 r= EAFNOSUPPORT; /* Zero host part */
207 if (r<0)
209 DIFBLOCK(1, r == EAFNOSUPPORT,
210 printf("bad destination: ");
211 writeIpAddr(ip_hdr->ih_dst);
212 printf("\n"));
213 bf_afree(data);
214 return r;
216 ip_hdr_chksum(ip_hdr, hdr_len);
218 data= bf_packIffLess(data, IP_MIN_HDR_SIZE);
219 assert (data->acc_length >= IP_MIN_HDR_SIZE);
220 ip_hdr= (ip_hdr_t *)ptr2acc_data(data);
222 if (ip_hdr->ih_flags_fragoff & HTONS(IH_DONT_FRAG))
224 req_mtu= bf_bufsize(data);
225 if (req_mtu > ip_port->ip_mtu)
227 DBLOCK(1, printf(
228 "packet is larger than link MTU and DF is set\n"));
229 bf_afree(data);
230 return EPACKSIZE;
233 else
234 req_mtu= 0;
236 addrInBytes= (u8_t *)&dstaddr;
238 if ((addrInBytes[0] & 0xff) == 0x7f) /* local loopback */
240 assert (data->acc_linkC == 1);
241 dstaddr= ip_hdr->ih_dst; /* swap src and dst
242 * addresses */
243 ip_hdr->ih_dst= ip_hdr->ih_src;
244 ip_hdr->ih_src= dstaddr;
245 data->acc_ext_link= NULL;
246 if (ip_port->ip_loopb_head == NULL)
248 ip_port->ip_loopb_head= data;
249 arg.ev_ptr= ip_port;
250 ev_enqueue(&ip_port->ip_loopb_event,
251 ip_process_loopb, arg);
253 else
254 ip_port->ip_loopb_tail->acc_ext_link= data;
255 ip_port->ip_loopb_tail= data;
257 return NW_OK;
260 if ((dstaddr & HTONL(0xe0000000)) == HTONL(0xe0000000))
262 if (dstaddr == (ipaddr_t)-1)
264 r= (*ip_port->ip_dev_send)(ip_port, dstaddr, data,
265 IP_LT_BROADCAST);
266 return r;
268 if (ip_nettype(dstaddr) == IPNT_CLASS_D)
270 /* Multicast, what about multicast routing? */
271 r= (*ip_port->ip_dev_send)(ip_port, dstaddr, data,
272 IP_LT_MULTICAST);
273 return r;
277 if (dstaddr == my_ipaddr)
279 assert (data->acc_linkC == 1);
281 data->acc_ext_link= NULL;
282 if (ip_port->ip_loopb_head == NULL)
284 ip_port->ip_loopb_head= data;
285 arg.ev_ptr= ip_port;
286 ev_enqueue(&ip_port->ip_loopb_event,
287 ip_process_loopb, arg);
289 else
290 ip_port->ip_loopb_tail->acc_ext_link= data;
291 ip_port->ip_loopb_tail= data;
293 return NW_OK;
296 if (((dstaddr ^ my_ipaddr) & netmask) == 0)
298 type= ((dstaddr == (my_ipaddr | ~netmask) &&
299 (ip_port->ip_flags & IPF_SUBNET_BCAST)) ?
300 IP_LT_BROADCAST : IP_LT_NORMAL);
302 r= (*ip_port->ip_dev_send)(ip_port, dstaddr, data, type);
303 return r;
306 r= oroute_frag (ip_port - ip_port_table, dstaddr, ttl, req_mtu,
307 &nexthop);
309 if (r == NW_OK)
311 if (nexthop == ip_port->ip_ipaddr)
313 data->acc_ext_link= NULL;
314 if (ip_port->ip_loopb_head == NULL)
316 ip_port->ip_loopb_head= data;
317 arg.ev_ptr= ip_port;
318 ev_enqueue(&ip_port->ip_loopb_event,
319 ip_process_loopb, arg);
321 else
322 ip_port->ip_loopb_tail->acc_ext_link= data;
323 ip_port->ip_loopb_tail= data;
325 else
327 r= (*ip_port->ip_dev_send)(ip_port,
328 nexthop, data, IP_LT_NORMAL);
331 else
333 DBLOCK(0x10, printf("got error %d\n", r));
334 bf_afree(data);
336 return r;
339 void ip_hdr_chksum(ip_hdr, ip_hdr_len)
340 ip_hdr_t *ip_hdr;
341 int ip_hdr_len;
343 ip_hdr->ih_hdr_chk= 0;
344 ip_hdr->ih_hdr_chk= ~oneC_sum (0, (u16_t *)ip_hdr, ip_hdr_len);
347 acc_t *ip_split_pack (ip_port, ref_last, mtu)
348 ip_port_t *ip_port;
349 acc_t **ref_last;
350 int mtu;
352 int pack_siz;
353 ip_hdr_t *first_hdr, *second_hdr;
354 int first_hdr_len, second_hdr_len;
355 int first_data_len, second_data_len;
356 int data_len, max_data_len, nfrags, new_first_data_len;
357 int first_opt_size, second_opt_size;
358 acc_t *first_pack, *second_pack, *tmp_pack;
359 u8_t *first_optptr, *second_optptr;
360 int i, optlen;
362 first_pack= *ref_last;
363 *ref_last= 0;
364 second_pack= 0;
366 first_pack= bf_align(first_pack, IP_MIN_HDR_SIZE, 4);
367 first_pack= bf_packIffLess(first_pack, IP_MIN_HDR_SIZE);
368 assert (first_pack->acc_length >= IP_MIN_HDR_SIZE);
370 first_hdr= (ip_hdr_t *)ptr2acc_data(first_pack);
371 first_hdr_len= (first_hdr->ih_vers_ihl & IH_IHL_MASK) * 4;
372 if (first_hdr_len>IP_MIN_HDR_SIZE)
374 first_pack= bf_packIffLess(first_pack, first_hdr_len);
375 first_hdr= (ip_hdr_t *)ptr2acc_data(first_pack);
378 pack_siz= bf_bufsize(first_pack);
379 assert(pack_siz > mtu);
381 assert (!(first_hdr->ih_flags_fragoff & HTONS(IH_DONT_FRAG)));
383 if (first_pack->acc_linkC != 1 ||
384 first_pack->acc_buffer->buf_linkC != 1)
386 /* Get a private copy of the IP header */
387 tmp_pack= bf_memreq(first_hdr_len);
388 memcpy(ptr2acc_data(tmp_pack), first_hdr, first_hdr_len);
389 first_pack= bf_delhead(first_pack, first_hdr_len);
390 tmp_pack->acc_next= first_pack;
391 first_pack= tmp_pack; tmp_pack= NULL;
392 first_hdr= (ip_hdr_t *)ptr2acc_data(first_pack);
395 data_len= ntohs(first_hdr->ih_length) - first_hdr_len;
397 /* Try to split the packet evenly. */
398 assert(mtu > first_hdr_len);
399 max_data_len= mtu-first_hdr_len;
400 nfrags= (data_len/max_data_len)+1;
401 new_first_data_len= data_len/nfrags;
402 if (new_first_data_len < 8)
404 /* Special case for extremely small MTUs */
405 new_first_data_len= 8;
407 new_first_data_len &= ~7; /* data goes in 8 byte chuncks */
409 assert(new_first_data_len >= 8);
410 assert(new_first_data_len+first_hdr_len <= mtu);
412 second_data_len= data_len-new_first_data_len;
413 second_pack= bf_cut(first_pack, first_hdr_len+
414 new_first_data_len, second_data_len);
415 tmp_pack= first_pack;
416 first_data_len= new_first_data_len;
417 first_pack= bf_cut (tmp_pack, 0, first_hdr_len+first_data_len);
418 bf_afree(tmp_pack);
419 tmp_pack= bf_memreq(first_hdr_len);
420 tmp_pack->acc_next= second_pack;
421 second_pack= tmp_pack;
422 second_hdr= (ip_hdr_t *)ptr2acc_data(second_pack);
423 *second_hdr= *first_hdr;
424 second_hdr->ih_flags_fragoff= htons(
425 ntohs(first_hdr->ih_flags_fragoff)+(first_data_len/8));
427 first_opt_size= first_hdr_len-IP_MIN_HDR_SIZE;
428 second_opt_size= 0;
429 if (first_opt_size)
431 first_pack= bf_packIffLess (first_pack,
432 first_hdr_len);
433 first_hdr= (ip_hdr_t *)ptr2acc_data(first_pack);
434 assert (first_pack->acc_length>=first_hdr_len);
435 first_optptr= (u8_t *)ptr2acc_data(first_pack)+
436 IP_MIN_HDR_SIZE;
437 second_optptr= (u8_t *)ptr2acc_data(
438 second_pack)+IP_MIN_HDR_SIZE;
439 i= 0;
440 while (i<first_opt_size)
442 switch (*first_optptr & IP_OPT_NUMBER)
444 case 0:
445 case 1:
446 optlen= 1;
447 break;
448 default:
449 optlen= first_optptr[1];
450 break;
452 assert (i + optlen <= first_opt_size);
453 i += optlen;
454 if (*first_optptr & IP_OPT_COPIED)
456 second_opt_size += optlen;
457 while (optlen--)
458 *second_optptr++=
459 *first_optptr++;
461 else
462 first_optptr += optlen;
464 while (second_opt_size & 3)
466 *second_optptr++= 0;
467 second_opt_size++;
470 second_hdr_len= IP_MIN_HDR_SIZE + second_opt_size;
472 second_hdr->ih_vers_ihl= (second_hdr->ih_vers_ihl & 0xf0)
473 + (second_hdr_len/4);
474 second_hdr->ih_length= htons(second_data_len+
475 second_hdr_len);
476 second_pack->acc_length= second_hdr_len;
478 assert(first_pack->acc_linkC == 1);
479 assert(first_pack->acc_buffer->buf_linkC == 1);
481 first_hdr->ih_flags_fragoff |= HTONS(IH_MORE_FRAGS);
482 first_hdr->ih_length= htons(first_data_len+
483 first_hdr_len);
484 assert (!(second_hdr->ih_flags_fragoff & HTONS(IH_DONT_FRAG)));
486 ip_hdr_chksum(first_hdr, first_hdr_len);
487 if (second_data_len+second_hdr_len <= mtu)
489 /* second_pack will not be split any further, so we have to
490 * calculate the header checksum.
492 ip_hdr_chksum(second_hdr, second_hdr_len);
495 *ref_last= second_pack;
497 return first_pack;
500 static void error_reply (ip_fd, error)
501 ip_fd_t *ip_fd;
502 int error;
504 if ((*ip_fd->if_get_userdata)(ip_fd->if_srfd, (size_t)error,
505 (size_t)0, FALSE))
507 ip_panic(( "can't error_reply" ));
512 * $PchId: ip_write.c,v 1.22 2004/08/03 11:11:04 philip Exp $