etc: simple typo fixes in kamailio.cfg
[sip-router] / raw_sock.c
1 /* 
2  * Copyright (C) 2010 iptelorg GmbH
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16
17 /** Kamailio core :: raw socket functions.
18  *  @file raw_sock.c
19  *  @ingroup core
20  *  Module: @ref core
21  */
22
23 #ifdef USE_RAW_SOCKS
24
25 #include "compiler_opt.h"
26 #include "ip_addr.h"
27 #include "dprint.h"
28 #include "str.h"
29 #include "rand/fastrand.h"
30 #include "globals.h"
31
32 #include <errno.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <sys/types.h>
36 #include <fcntl.h>
37 #include <sys/socket.h>
38 #include <netinet/in.h>
39 #include <netinet/in_systm.h>
40 #include <arpa/inet.h>
41 #ifndef __USE_BSD
42 #define __USE_BSD  /* on linux use bsd version of iphdr (more portable) */
43 #endif /* __USE_BSD */
44 #include <netinet/ip.h>
45 #define __FAVOR_BSD /* on linux use bsd version of udphdr (more portable) */
46 #include <netinet/udp.h>
47
48 #include "raw_sock.h"
49 #include "cfg/cfg.h"
50 #include "cfg_core.h"
51
52
53 #if defined (__OS_freebsd) || defined (__OS_netbsd) || defined(__OS_openbsd) \
54         || defined (__OS_darwin)
55 /** fragmentation is done by the kernel (no need to do it in userspace) */
56 #define RAW_IPHDR_INC_AUTO_FRAG
57 #endif /* __OS_* */
58
59 /* macros for converting values in the expected format */
60 #if defined (__OS_freebsd) || defined (__OS_netbsd) || defined (__OS_darwin)
61 /* on freebsd and netbsd the ip offset (along with flags) and the
62    ip header length must be filled in _host_ bytes order format.
63    The same is true for openbsd < 2.1.
64 */
65 /** convert the ip offset in the format expected by the kernel. */
66 #define RAW_IPHDR_IP_OFF(off) (unsigned short)(off)
67 /** convert the ip total length in the format expected by the kernel. */
68 #define RAW_IPHDR_IP_LEN(tlen) (unsigned short)(tlen)
69
70 #else /* __OS_* */
71 /* linux, openbsd >= 2.1 a.s.o. */
72 /** convert the ip offset in the format expected by the kernel. */
73 #define RAW_IPHDR_IP_OFF(off)  htons((unsigned short)(off))
74 /** convert the ip total length in the format expected by the kernel. */
75 #define RAW_IPHDR_IP_LEN(tlen) htons((unsigned short)(tlen))
76
77 #endif /* __OS_* */
78
79 int raw_ipip = 0; /* set if raw socket is in capture mode for IPIP */
80
81
82 /** create and return a raw socket.
83  * @param proto - protocol used (e.g. IPPROTO_UDP, IPPROTO_RAW)
84  * @param ip - if not null the socket will be bound on this ip.
85  * @param iface - if not null the socket will be bound to this interface
86  *                (SO_BINDTODEVICE). This is supported only on linux.
87  * @param iphdr_incl - set to 1 if packets send on this socket include
88  *                     a pre-built ip header (some fields, like the checksum
89  *                     will still be filled by the kernel, OTOH packet
90  *                     fragmentation has to be done in user space).
91  * @return socket on success, -1 on error
92  */
93 int raw_socket(int proto, struct ip_addr* ip, str* iface, int iphdr_incl)
94 {
95         int sock;
96         int t;
97         union sockaddr_union su;
98 #if defined (SO_BINDTODEVICE)
99         char short_ifname[sizeof(int)];
100         int ifname_len;
101         char* ifname;
102 #endif /* SO_BINDTODEVICE */
103
104         sock = socket(PF_INET, SOCK_RAW, proto);
105         if (sock==-1)
106                 goto error;
107         /* set socket options */
108         if (iphdr_incl) {
109                 t=1;
110                 if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, &t, sizeof(t))<0){
111                         ERR("raw_socket: setsockopt(IP_HDRINCL) failed: %s [%d]\n",
112                                         strerror(errno), errno);
113                         goto error;
114                 }
115         } else {
116                 /* IP_PKTINFO makes no sense if the ip header is included */
117                 /* using IP_PKTINFO */
118                 t=1;
119 #ifdef IP_PKTINFO
120                 if (setsockopt(sock, IPPROTO_IP, IP_PKTINFO, &t, sizeof(t))<0){
121                         ERR("raw_socket: setsockopt(IP_PKTINFO) failed: %s [%d]\n",
122                                         strerror(errno), errno);
123                         goto error;
124                 }
125 #elif defined(IP_RECVDSTADDR)
126                 if (setsockopt(sock, IPPROTO_IP, IP_RECVDSTADDR, &t, sizeof(t))<0){
127                         ERR("raw_socket: setsockop(IP_RECVDSTADDR) failed: %s [%d]\n",
128                                         strerror(errno), errno);
129                         goto error;
130                 }
131 #else
132 #error "no method of getting the destination ip address supported"
133 #endif /* IP_RECVDSTADDR / IP_PKTINFO */
134         }
135 #if defined (IP_MTU_DISCOVER) && defined (IP_PMTUDISC_DONT)
136         t=IP_PMTUDISC_DONT;
137         if(setsockopt(sock, IPPROTO_IP, IP_MTU_DISCOVER, &t, sizeof(t)) ==-1){
138                 ERR("raw_socket: setsockopt(IP_MTU_DISCOVER): %s\n",
139                                 strerror(errno));
140                 goto error;
141         }
142 #endif /* IP_MTU_DISCOVER && IP_PMTUDISC_DONT */
143         if (iface && iface->s){
144 #if defined (SO_BINDTODEVICE)
145                 /* workaround for linux bug: arg to setsockopt must have at least
146                  * sizeof(int) size or EINVAL would be returned */
147                 if (iface->len<sizeof(int)){
148                         memcpy(short_ifname, iface->s, iface->len);
149                         short_ifname[iface->len]=0; /* make sure it's zero term */
150                         ifname_len=sizeof(short_ifname);
151                         ifname=short_ifname;
152                 }else{
153                         ifname_len=iface->len;
154                         ifname=iface->s;
155                 }
156                 if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, ifname, ifname_len)
157                                                 <0){
158                                 ERR("raw_socket: could not bind to %.*s: %s [%d]\n",
159                                                         iface->len, ZSW(iface->s), strerror(errno), errno);
160                                 goto error;
161                 }
162 #else /* !SO_BINDTODEVICE */
163                 /* SO_BINDTODEVICE is linux specific => cannot bind to a device */
164                 ERR("raw_socket: bind to device supported only on linux\n");
165                 goto error;
166 #endif /* SO_BINDTODEVICE */
167         }
168         /* FIXME: probe_max_receive_buffer(sock) missing */
169         if (ip){
170                 init_su(&su, ip, 0);
171                 if (bind(sock, &su.s, sockaddru_len(su))==-1){
172                         ERR("raw_socket: bind(%s) failed: %s [%d]\n",
173                                 ip_addr2a(ip), strerror(errno), errno);
174                         goto error;
175                 }
176         }
177         return sock;
178 error:
179         if (sock!=-1) close(sock);
180         return -1;
181 }
182
183
184
185 /** create and return an udp over ipv4  raw socket.
186  * @param ip - if not null the socket will be bound on this ip.
187  * @param iface - if not null the socket will be bound to this interface
188  *                (SO_BINDTODEVICE).
189  * @param iphdr_incl - set to 1 if packets send on this socket include
190  *                     a pre-built ip header (some fields, like the checksum
191  *                     will still be filled by the kernel, OTOH packet
192  *                     fragmentation has to be done in user space).
193  * @return socket on success, -1 on error
194  */
195 int raw_udp4_socket(struct ip_addr* ip, str* iface, int iphdr_incl)
196 {
197         return raw_socket(IPPROTO_UDP, ip, iface, iphdr_incl);
198 }
199
200
201
202 /** receives an ipv4 packet using a raw socket.
203  * An ipv4 packet is received in buf, using IP_PKTINFO or IP_RECVDSTADDR.
204  * from and to are filled (only the ip part the ports are 0 since this
205  * function doesn't try to look beyond the IP level).
206  * @param sock - raw socket
207  * @param buf - detination buffer.
208  * @param len - buffer len (should be enough for receiving a packet +
209  *               IP header).
210  * @param from - result parameter, the IP address part of it will be filled
211  *                with the source address and the port with 0.
212  * @param to - result parameter, the IP address part of it will be filled
213  *                with the destination (local) address and the port with 0.
214  * @return packet len or <0 on error: -1 (check errno),
215  *        -2 no IP_PKTINFO/IP_RECVDSTADDR found or AF mismatch
216  */
217 int recvpkt4(int sock, char* buf, int len, union sockaddr_union* from,
218                                         union sockaddr_union* to)
219 {
220         struct iovec iov[1];
221         struct msghdr rcv_msg;
222         struct cmsghdr* cmsg;
223 #ifdef IP_PKTINFO
224         struct in_pktinfo* rcv_pktinfo;
225 #endif /* IP_PKTINFO */
226         int n, ret;
227         char msg_ctrl_buf[1024];
228
229         iov[0].iov_base=buf;
230         iov[0].iov_len=len;
231         rcv_msg.msg_name=from;
232         rcv_msg.msg_namelen=sockaddru_len(*from);
233         rcv_msg.msg_control=msg_ctrl_buf;
234         rcv_msg.msg_controllen=sizeof(msg_ctrl_buf);
235         rcv_msg.msg_iov=&iov[0];
236         rcv_msg.msg_iovlen=1;
237         ret=-2; /* no PKT_INFO or AF mismatch */
238 retry:
239         n=recvmsg(sock, &rcv_msg, MSG_WAITALL);
240         if (unlikely(n==-1)){
241                 if (errno==EINTR)
242                         goto retry;
243                 ret=n;
244                 goto end;
245         }
246         /* find the pkt info */
247         for (cmsg=CMSG_FIRSTHDR(&rcv_msg); cmsg; cmsg=CMSG_NXTHDR(&rcv_msg, cmsg)){
248 #ifdef IP_PKTINFO
249                 if (likely((cmsg->cmsg_level==IPPROTO_IP) &&
250                                         (cmsg->cmsg_type==IP_PKTINFO))) {
251                         rcv_pktinfo=(struct in_pktinfo*)CMSG_DATA(cmsg);
252                         to->sin.sin_family=AF_INET;
253                         memcpy(&to->sin.sin_addr, &rcv_pktinfo->ipi_spec_dst.s_addr, 
254                                                                         sizeof(to->sin.sin_addr));
255                         to->sin.sin_port=0; /* not known */
256                         /* interface no. in ipi_ifindex */
257                         ret=n; /* success */
258                         break;
259                 }
260 #elif defined (IP_RECVDSTADDR)
261                 if (likely((cmsg->cmsg_level==IPPROTO_IP) &&
262                                         (cmsg->cmsg_type==IP_RECVDSTADDR))) {
263                         to->sin.sin_family=AF_INET;
264                         memcpy(&to->sin.sin_addr, CMSG_DATA(cmsg),
265                                                                         sizeof(to->sin.sin_addr));
266                         to->sin.sin_port=0; /* not known */
267                         ret=n; /* success */
268                         break;
269                 }
270 #else
271 #error "no method of getting the destination ip address supported"
272 #endif /* IP_PKTINFO / IP_RECVDSTADDR */
273         }
274 end:
275         return ret;
276 }
277
278
279
280 /* receive an ipv4 udp packet over a raw socket.
281  * The packet is copied in *buf and *buf is advanced to point to the
282  * payload.  Fills from and to.
283  * @param rsock - raw socket
284  * @param buf - the packet will be written to where *buf points intially and
285  *              then *buf will be advanced to point to the udp payload.
286  * @param len - buffer length (should be enough to hold at least the
287  *               ip and udp headers + 1 byte).
288  * @param from - result parameter, filled with source address and port of the
289  *               packet.
290  * @param from - result parameter, filled with destination (local) address and
291  *               port of the packet.
292  * @param rf   - filter used to decide whether or not the packet is
293  *                accepted/processed. If null, all the packets are accepted.
294  * @return packet len or  <0 on error (-1 and -2 on recv error @see recvpkt4,
295  *         -3 if the headers are invalid and -4 if the packet doesn't
296  *         match the  filter).
297  */
298 int raw_udp4_recv(int rsock, char** buf, int len, union sockaddr_union* from,
299                                         union sockaddr_union* to, struct raw_filter* rf)
300 {
301         int n;
302         unsigned short dst_port;
303         unsigned short src_port;
304         struct ip_addr dst_ip;
305         char* end;
306         char* udph_start;
307         char* udp_payload;
308         struct ip iph;
309         struct udphdr udph;
310         unsigned short udp_len;
311
312         n=recvpkt4(rsock, *buf, len, from, to);
313         if (unlikely(n<0)) goto error;
314         
315         end=*buf+n;
316         if (unlikely(n<((sizeof(struct ip) * raw_ipip ? 2 : 1)+sizeof(struct udphdr)))) {
317                 n=-3;
318                 goto error;
319         }
320         
321         if(raw_ipip) 
322                 *buf = *buf + sizeof(struct ip);
323         
324         /* FIXME: if initial buffer is aligned, one could skip the memcpy
325            and directly cast ip and udphdr pointer to the memory */
326         memcpy(&iph, *buf, sizeof(struct ip));
327         udph_start=*buf+iph.ip_hl*4;
328         udp_payload=udph_start+sizeof(struct udphdr);
329         if (unlikely(udp_payload>end)){
330                 n=-3;
331                 goto error;
332         }
333         memcpy(&udph, udph_start, sizeof(struct udphdr));
334         udp_len=ntohs(udph.uh_ulen);
335         if (unlikely((udph_start+udp_len)!=end)){
336                 if ((udph_start+udp_len)>end){
337                         n=-3;
338                         goto error;
339                 }else{
340                         ERR("udp length too small: %d/%d\n",
341                                         (int)udp_len, (int)(end-udph_start));
342                         n=-3;
343                         goto error;
344                 }
345         }
346         /* advance buf */
347         *buf=udp_payload;
348         n=(int)(end-*buf);
349         /* fill ip from the packet (needed if no PKT_INFO is used) */
350         dst_ip.af=AF_INET;
351         dst_ip.len=4;
352         dst_ip.u.addr32[0]=iph.ip_dst.s_addr;
353         /* fill dst_port */
354         dst_port=ntohs(udph.uh_dport);
355         ip_addr2su(to, &dst_ip, dst_port);
356         /* fill src_port */
357         src_port=ntohs(udph.uh_sport);
358         su_setport(from, src_port);
359         if (likely(rf)) {
360                 su2ip_addr(&dst_ip, to);
361                 if ( (dst_port && rf->port1 && ((dst_port<rf->port1) ||
362                                                                                 (dst_port>rf->port2)) ) ||
363                         (matchnet(&dst_ip, &rf->dst)!=1) ){
364                         /* no match */
365                         n=-4;
366                         goto error;
367                 }
368         }
369         
370 error:
371         return n;
372 }
373
374
375
376 /** udp checksum helper: compute the pseudo-header 16-bit "sum".
377  * Computes the partial checksum (no complement) of the pseudo-header.
378  * It is meant to be used by udpv4_chksum().
379  * @param uh - filled udp header
380  * @param src - source ip address in network byte order.
381  * @param dst - destination ip address in network byte order.
382  * @param length - payload length (not including the udp header),
383  *                 in _host_ order.
384  * @return the partial checksum in host order
385  */
386 static inline unsigned short udpv4_vhdr_sum(    struct udphdr* uh,
387                                                                                 struct in_addr* src,
388                                                                                 struct in_addr* dst,
389                                                                                 unsigned short length)
390 {
391         unsigned sum;
392         
393         /* pseudo header */
394         sum=(src->s_addr>>16)+(src->s_addr&0xffff)+
395                 (dst->s_addr>>16)+(dst->s_addr&0xffff)+
396                 htons(IPPROTO_UDP)+(uh->uh_ulen);
397         /* udp header */
398         sum+=(uh->uh_dport)+(uh->uh_sport)+(uh->uh_ulen) + 0 /*chksum*/; 
399         /* fold it */
400         sum=(sum>>16)+(sum&0xffff);
401         sum+=(sum>>16);
402         /* no complement */
403         return ntohs((unsigned short) sum);
404 }
405
406
407
408 /** compute the udp over ipv4 checksum.
409  * @param u - filled udp header (except checksum).
410  * @param src - source ip v4 address, in _network_ byte order.
411  * @param dst - destination ip v4 address, int _network_ byte order.
412  * @param data - pointer to the udp payload.
413  * @param length - payload length, not including the udp header and in
414  *                 _host_ order. The length mist be <= 0xffff - 8
415  *                 (to allow space for the udp header).
416  * @return the checksum in _host_ order */
417 inline static unsigned short udpv4_chksum(struct udphdr* u,
418                                                         struct in_addr* src, struct in_addr* dst,
419                                                         unsigned char* data, unsigned short length)
420 {
421         unsigned sum;
422         unsigned char* end;
423         sum=udpv4_vhdr_sum(u, src, dst, length);
424         end=data+(length&(~0x1)); /* make sure it's even */
425         /* TODO: 16 & 32 bit aligned version */
426                 /* not aligned */
427                 for(;data<end;data+=2){
428                         sum+=((data[0]<<8)+data[1]);
429                 }
430                 if (length&0x1)
431                         sum+=((*data)<<8);
432         
433         /* fold it */
434         sum=(sum>>16)+(sum&0xffff);
435         sum+=(sum>>16);
436         return (unsigned short)~sum;
437 }
438
439
440
441 /** fill in an udp header.
442  * @param u - udp header that will be filled.
443  * @param from - source ip v4 address and port.
444  * @param to -   destination ip v4 address and port.
445  * @param buf - pointer to the payload.
446  * @param len - payload length (not including the udp header).
447  * @param do_chk - if set the udp checksum will be computed, else it will
448  *                 be set to 0.
449  * @return 0 on success, < 0 on error.
450  */
451 inline static int mk_udp_hdr(struct udphdr* u, struct sockaddr_in* from, 
452                                 struct sockaddr_in* to, unsigned char* buf, int len,
453                                         int do_chk)
454 {
455         u->uh_ulen=htons((unsigned short)len+sizeof(struct udphdr));
456         u->uh_sport=from->sin_port;
457         u->uh_dport=to->sin_port;
458         if (do_chk)
459                 u->uh_sum=htons(
460                                 udpv4_chksum(u, &from->sin_addr, &to->sin_addr,  buf, len));
461         else
462                 u->uh_sum=0; /* no checksum */
463         return 0;
464 }
465
466
467
468 /** fill in an ip header.
469  * Note: the checksum is _not_ computed.
470  * WARNING: The ip header length and offset might be filled in
471  * _host_ byte order or network byte order (depending on the OS, for example
472  *  freebsd needs host byte order for raw sockets with IPHDR_INC, while
473  *  linux needs network byte order).
474  * @param iph - ip header that will be filled.
475  * @param from - source ip v4 address (network byte order).
476  * @param to -   destination ip v4 address (network byte order).
477  * @param payload len - payload length (not including the ip header).
478  * @param proto - protocol.
479  * @return 0 on success, < 0 on error.
480  */
481 inline static int mk_ip_hdr(struct ip* iph, struct in_addr* from,
482                                 struct in_addr* to, int payload_len, unsigned char proto)
483 {
484         iph->ip_hl = sizeof(struct ip)/4;
485         iph->ip_v = 4;
486         iph->ip_tos = tos;
487         /* on freebsd ip_len _must_ be in _host_ byte order instead
488            of network byte order. On linux the length is ignored (it's filled
489            automatically every time). */
490         iph->ip_len = RAW_IPHDR_IP_LEN(payload_len + sizeof(struct ip));
491         iph->ip_id = 0; /* 0 => will be filled automatically by the kernel */
492         iph->ip_off = 0; /* frag.: first 3 bits=flags=0, last 13 bits=offset */
493         iph->ip_ttl = cfg_get(core, core_cfg, udp4_raw_ttl);
494         iph->ip_p = proto;
495         iph->ip_src = *from;
496         iph->ip_dst = *to;
497         iph->ip_sum = 0;
498
499         return 0;
500 }
501
502
503
504 /** send an udp packet over a non-ip_hdrincl raw socket.
505  * @param rsock - raw socket
506  * @param buf - data
507  * @param len - data len
508  * @param from - source address:port (_must_ be non-null, but the ip address
509  *                can be 0, in which case it will be filled by the kernel).
510  * @param to - destination address:port
511  * @return  <0 on error (errno set too), number of bytes sent on success
512  *          (including the udp header => on success len + udpheader size).
513  */
514 int raw_udp4_send(int rsock, char* buf, unsigned int len,
515                                         union sockaddr_union* from,
516                                         union sockaddr_union* to)
517 {
518         struct msghdr snd_msg;
519         struct cmsghdr* cmsg;
520 #ifdef IP_PKTINFO
521         struct in_pktinfo* snd_pktinfo;
522 #endif /* IP_PKTINFO */
523         struct iovec iov[2];
524         struct udphdr udp_hdr;
525         char msg_ctrl_snd_buf[1024];
526         int ret;
527
528         memset(&snd_msg, 0, sizeof(snd_msg));
529         snd_msg.msg_name=&to->sin;
530         snd_msg.msg_namelen=sockaddru_len(*to);
531         snd_msg.msg_iov=&iov[0];
532         /* prepare udp header */
533         mk_udp_hdr(&udp_hdr, &from->sin, &to->sin, (unsigned char*)buf, len, 1);
534         iov[0].iov_base=(char*)&udp_hdr;
535         iov[0].iov_len=sizeof(udp_hdr);
536         iov[1].iov_base=buf;
537         iov[1].iov_len=len;
538         snd_msg.msg_iovlen=2;
539         snd_msg.msg_control=msg_ctrl_snd_buf;
540         snd_msg.msg_controllen=sizeof(msg_ctrl_snd_buf);
541         /* init pktinfo cmsg */
542         cmsg=CMSG_FIRSTHDR(&snd_msg);
543         cmsg->cmsg_level=IPPROTO_IP;
544 #ifdef IP_PKTINFO
545         cmsg->cmsg_type=IP_PKTINFO;
546         cmsg->cmsg_len=CMSG_LEN(sizeof(struct in_pktinfo));
547         snd_pktinfo=(struct in_pktinfo*)CMSG_DATA(cmsg);
548         snd_pktinfo->ipi_ifindex=0;
549         snd_pktinfo->ipi_spec_dst.s_addr=from->sin.sin_addr.s_addr;
550 #elif defined (IP_SENDSRCADDR)
551         cmsg->cmsg_type=IP_SENDSRCADDR;
552         cmsg->cmsg_len=CMSG_LEN(sizeof(struct in_addr));
553         memcpy(CMSG_DATA(cmsg), &from->sin.sin_addr.s_addr,
554                                                         sizeof(struct in_addr));
555 #else
556 #error "no method of setting the source ip supported"
557 #endif /* IP_PKTINFO / IP_SENDSRCADDR */
558         snd_msg.msg_controllen=cmsg->cmsg_len;
559         snd_msg.msg_flags=0;
560         ret=sendmsg(rsock, &snd_msg, 0);
561         return ret;
562 }
563
564
565
566 /** send an udp packet over an IP_HDRINCL raw socket.
567  * If needed, send several fragments.
568  * @param rsock - raw socket
569  * @param buf - data
570  * @param len - data len
571  * @param from - source address:port (_must_ be non-null, but the ip address
572  *                can be 0, in which case it will be filled by the kernel).
573  * @param to - destination address:port
574  * @param mtu - maximum datagram size (including the ip header, excluding
575  *              link layer headers). Minimum allowed size is 28
576  *               (sizeof(ip_header + udp_header)). If mtu is lower, it will
577  *               be ignored (the packet will be sent un-fragmented).
578  *              0 can be used to disable fragmentation.
579  * @return  <0 on error (-2: datagram too big, -1: check errno),
580  *          number of bytes sent on success
581  *          (including the ip & udp headers =>
582  *               on success len + udpheader + ipheader size).
583  */
584 int raw_iphdr_udp4_send(int rsock, char* buf, unsigned int len,
585                                                 union sockaddr_union* from,
586                                                 union sockaddr_union* to, unsigned short mtu)
587 {
588         struct msghdr snd_msg;
589         struct iovec iov[2];
590         struct ip_udp_hdr {
591                 struct ip ip;
592                 struct udphdr udp;
593         } hdr;
594         unsigned int totlen;
595 #ifndef RAW_IPHDR_INC_AUTO_FRAG
596         unsigned int ip_frag_size; /* fragment size */
597         unsigned int last_frag_extra; /* extra bytes possible in the last frag */
598         unsigned int ip_payload;
599         unsigned int last_frag_offs;
600         void* last_frag_start;
601         int frg_no;
602 #endif /* RAW_IPHDR_INC_AUTO_FRAG */
603         int ret;
604
605         totlen = len + sizeof(hdr);
606         if (unlikely(totlen) > 65535)
607                 return -2;
608         memset(&snd_msg, 0, sizeof(snd_msg));
609         snd_msg.msg_name=&to->sin;
610         snd_msg.msg_namelen=sockaddru_len(*to);
611         snd_msg.msg_iov=&iov[0];
612         /* prepare the udp & ip headers */
613         mk_udp_hdr(&hdr.udp, &from->sin, &to->sin, (unsigned char*)buf, len, 1);
614         mk_ip_hdr(&hdr.ip, &from->sin.sin_addr, &to->sin.sin_addr,
615                                 len + sizeof(hdr.udp), IPPROTO_UDP);
616         iov[0].iov_base=(char*)&hdr;
617         iov[0].iov_len=sizeof(hdr);
618         snd_msg.msg_iovlen=2;
619         snd_msg.msg_control=0;
620         snd_msg.msg_controllen=0;
621         snd_msg.msg_flags=0;
622         /* this part changes for different fragments */
623         /* packets are fragmented if mtu has a valid value (at least an
624            IP header + UDP header fit in it) and if the total length is greater
625            then the mtu */
626 #ifndef RAW_IPHDR_INC_AUTO_FRAG
627         if (likely(totlen <= mtu || mtu <= sizeof(hdr))) {
628 #endif /* RAW_IPHDR_INC_AUTO_FRAG */
629                 iov[1].iov_base=buf;
630                 iov[1].iov_len=len;
631                 ret=sendmsg(rsock, &snd_msg, 0);
632 #ifndef RAW_IPHDR_INC_AUTO_FRAG
633         } else {
634                 ip_payload = len + sizeof(hdr.udp);
635                 /* a fragment offset must be a multiple of 8 => its size must
636                    also be a multiple of 8, except for the last fragment */
637                 ip_frag_size = (mtu -sizeof(hdr.ip)) & (~7);
638                 last_frag_extra = (mtu - sizeof(hdr.ip)) & 7; /* rest */
639                 frg_no = ip_payload / ip_frag_size +
640                                  ((ip_payload % ip_frag_size) > last_frag_extra);
641                 /*ip_last_frag_size = ip_payload % frag_size +
642                                                         ((ip_payload % frag_size) <= last_frag_extra) *
643                                                         ip_frag_size; */
644                 last_frag_offs = (frg_no - 1) * ip_frag_size;
645                 /* if we are here mtu => sizeof(ip_h+udp_h) && payload > mtu
646                    => last_frag_offs >= sizeof(hdr.udp) */
647                 last_frag_start = buf + last_frag_offs - sizeof(hdr.udp);
648                 hdr.ip.ip_id = fastrand_max(65534) + 1; /* random id, should be != 0
649                                                                                           (if 0 the kernel will fill it) */
650                 /* send the first fragment */
651                 iov[1].iov_base=buf;
652                 /* ip_frag_size >= sizeof(hdr.udp) because we are here only
653                    if mtu >= sizeof(hdr.ip) + sizeof(hdr.udp) */
654                 iov[1].iov_len=ip_frag_size - sizeof(hdr.udp);
655                 hdr.ip.ip_len = RAW_IPHDR_IP_LEN(ip_frag_size + sizeof(hdr.ip));
656                 hdr.ip.ip_off = RAW_IPHDR_IP_OFF(0x2000); /* set MF */
657                 ret=sendmsg(rsock, &snd_msg, 0);
658                 if (unlikely(ret < 0))
659                         goto end;
660                 /* all the other fragments, include only the ip header */
661                 iov[0].iov_len = sizeof(hdr.ip);
662                 iov[1].iov_base =  (char*)iov[1].iov_base + iov[1].iov_len;
663                 /* fragments between the first and the last */
664                 while(unlikely(iov[1].iov_base < last_frag_start)) {
665                         iov[1].iov_len = ip_frag_size;
666                         hdr.ip.ip_len = RAW_IPHDR_IP_LEN(iov[1].iov_len + sizeof(hdr.ip));
667                         /* set MF  */
668                         hdr.ip.ip_off = RAW_IPHDR_IP_OFF( (unsigned short)
669                                                                         (((char*)iov[1].iov_base - (char*)buf +
670                                                                                 sizeof(hdr.udp)) / 8) | 0x2000 );
671                         ret=sendmsg(rsock, &snd_msg, 0);
672                         if (unlikely(ret < 0))
673                                 goto end;
674                         iov[1].iov_base =  (char*)iov[1].iov_base + iov[1].iov_len;
675                 }
676                 /* last fragment */
677                 iov[1].iov_len = buf + len - (char*)iov[1].iov_base;
678                 hdr.ip.ip_len = RAW_IPHDR_IP_LEN(iov[1].iov_len + sizeof(hdr.ip));
679                 /* don't set MF (last fragment) */
680                 hdr.ip.ip_off = RAW_IPHDR_IP_OFF((unsigned short)
681                                                                         (((char*)iov[1].iov_base - (char*)buf +
682                                                                                 sizeof(hdr.udp)) / 8) );
683                 ret=sendmsg(rsock, &snd_msg, 0);
684                 if (unlikely(ret < 0))
685                         goto end;
686         }
687 end:
688 #endif /* RAW_IPHDR_INC_AUTO_FRAG */
689         return ret;
690 }
691
692
693
694 #endif /* USE_RAW_SOCKS */