Browse code

udp: raw socket support

To use this feature, set following in sems.conf:
use_raw_sockets=yes

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