1 /*
2  *	BIRD Internet Routing Daemon -- Linux Multicasting and Network Includes
3  *
4  *	(c) 1998--2000 Martin Mares <mj@ucw.cz>
5  *
6  *	Can be freely distributed and used under the terms of the GNU GPL.
7  */
8 
9 #ifndef IPV6_MINHOPCOUNT
10 #define IPV6_MINHOPCOUNT 73
11 #endif
12 
13 #ifndef TCP_MD5SIG_EXT
14 #define TCP_MD5SIG_EXT 32
15 #endif
16 
17 #ifndef TCP_MD5SIG_FLAG_PREFIX
18 #define TCP_MD5SIG_FLAG_PREFIX 1
19 #endif
20 
21 /* We redefine the tcp_md5sig structure with different name to avoid collision with older headers */
22 struct tcp_md5sig_ext {
23   struct  sockaddr_storage tcpm_addr;		/* Address associated */
24   u8    tcpm_flags;				/* Extension flags */
25   u8    tcpm_prefixlen;				/* Address prefix */
26   u16   tcpm_keylen;				/* Key length */
27   u32   __tcpm_pad2;				/* Zero */
28   u8    tcpm_key[TCP_MD5SIG_MAXKEYLEN];		/* Key (binary) */
29 };
30 
31 
32 /* Linux does not care if sa_len is larger than needed */
33 #define SA_LEN(x) sizeof(sockaddr)
34 
35 
36 /*
37  *	Linux IPv4 multicast syscalls
38  */
39 
40 #define INIT_MREQ4(maddr,ifa) \
41   { .imr_multiaddr = ipa_to_in4(maddr), .imr_ifindex = ifa->index }
42 
43 static inline int
sk_setup_multicast4(sock * s)44 sk_setup_multicast4(sock *s)
45 {
46   struct ip_mreqn mr = { .imr_ifindex = s->iface->index };
47   int ttl = s->ttl;
48   int n = 0;
49 
50   /* This defines where should we send _outgoing_ multicasts */
51   if (setsockopt(s->fd, SOL_IP, IP_MULTICAST_IF, &mr, sizeof(mr)) < 0)
52     ERR("IP_MULTICAST_IF");
53 
54   if (setsockopt(s->fd, SOL_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0)
55     ERR("IP_MULTICAST_TTL");
56 
57   if (setsockopt(s->fd, SOL_IP, IP_MULTICAST_LOOP, &n, sizeof(n)) < 0)
58     ERR("IP_MULTICAST_LOOP");
59 
60   return 0;
61 }
62 
63 static inline int
sk_join_group4(sock * s,ip_addr maddr)64 sk_join_group4(sock *s, ip_addr maddr)
65 {
66   struct ip_mreqn mr = INIT_MREQ4(maddr, s->iface);
67 
68   if (setsockopt(s->fd, SOL_IP, IP_ADD_MEMBERSHIP, &mr, sizeof(mr)) < 0)
69     ERR("IP_ADD_MEMBERSHIP");
70 
71   return 0;
72 }
73 
74 static inline int
sk_leave_group4(sock * s,ip_addr maddr)75 sk_leave_group4(sock *s, ip_addr maddr)
76 {
77   struct ip_mreqn mr = INIT_MREQ4(maddr, s->iface);
78 
79   if (setsockopt(s->fd, SOL_IP, IP_DROP_MEMBERSHIP, &mr, sizeof(mr)) < 0)
80     ERR("IP_DROP_MEMBERSHIP");
81 
82   return 0;
83 }
84 
85 
86 /*
87  *	Linux IPv4 packet control messages
88  */
89 
90 /* Mostly similar to standardized IPv6 code */
91 
92 #define CMSG4_SPACE_PKTINFO CMSG_SPACE(sizeof(struct in_pktinfo))
93 #define CMSG4_SPACE_TTL CMSG_SPACE(sizeof(int))
94 
95 static inline int
sk_request_cmsg4_pktinfo(sock * s)96 sk_request_cmsg4_pktinfo(sock *s)
97 {
98   int y = 1;
99 
100   if (setsockopt(s->fd, SOL_IP, IP_PKTINFO, &y, sizeof(y)) < 0)
101     ERR("IP_PKTINFO");
102 
103   return 0;
104 }
105 
106 static inline int
sk_request_cmsg4_ttl(sock * s)107 sk_request_cmsg4_ttl(sock *s)
108 {
109   int y = 1;
110 
111   if (setsockopt(s->fd, SOL_IP, IP_RECVTTL, &y, sizeof(y)) < 0)
112     ERR("IP_RECVTTL");
113 
114   return 0;
115 }
116 
117 static inline void
sk_process_cmsg4_pktinfo(sock * s,struct cmsghdr * cm)118 sk_process_cmsg4_pktinfo(sock *s, struct cmsghdr *cm)
119 {
120   if (cm->cmsg_type == IP_PKTINFO)
121   {
122     struct in_pktinfo *pi = (struct in_pktinfo *) CMSG_DATA(cm);
123     s->laddr = ipa_from_in4(pi->ipi_addr);
124     s->lifindex = pi->ipi_ifindex;
125   }
126 }
127 
128 static inline void
sk_process_cmsg4_ttl(sock * s,struct cmsghdr * cm)129 sk_process_cmsg4_ttl(sock *s, struct cmsghdr *cm)
130 {
131   if (cm->cmsg_type == IP_TTL)
132     s->rcv_ttl = * (int *) CMSG_DATA(cm);
133 }
134 
135 static inline void
sk_prepare_cmsgs4(sock * s,struct msghdr * msg,void * cbuf,size_t cbuflen)136 sk_prepare_cmsgs4(sock *s, struct msghdr *msg, void *cbuf, size_t cbuflen)
137 {
138   struct cmsghdr *cm;
139   struct in_pktinfo *pi;
140   int controllen = 0;
141 
142   msg->msg_control = cbuf;
143   msg->msg_controllen = cbuflen;
144 
145   cm = CMSG_FIRSTHDR(msg);
146   cm->cmsg_level = SOL_IP;
147   cm->cmsg_type = IP_PKTINFO;
148   cm->cmsg_len = CMSG_LEN(sizeof(*pi));
149   controllen += CMSG_SPACE(sizeof(*pi));
150 
151   pi = (struct in_pktinfo *) CMSG_DATA(cm);
152   pi->ipi_ifindex = s->iface ? s->iface->index : 0;
153   pi->ipi_spec_dst = ipa_to_in4(s->saddr);
154   pi->ipi_addr = ipa_to_in4(IPA_NONE);
155 
156   msg->msg_controllen = controllen;
157 }
158 
159 
160 /*
161  *	Miscellaneous Linux socket syscalls
162  */
163 
164 int
sk_set_md5_auth(sock * s,ip_addr local UNUSED,ip_addr remote,int pxlen,struct iface * ifa,const char * passwd,int setkey UNUSED)165 sk_set_md5_auth(sock *s, ip_addr local UNUSED, ip_addr remote, int pxlen, struct iface *ifa, const char *passwd, int setkey UNUSED)
166 {
167   struct tcp_md5sig_ext md5;
168 
169   memset(&md5, 0, sizeof(md5));
170   sockaddr_fill((sockaddr *) &md5.tcpm_addr, s->af, remote, ifa, 0);
171 
172   if (passwd)
173   {
174     int len = strlen(passwd);
175 
176     if (len > TCP_MD5SIG_MAXKEYLEN)
177       ERR_MSG("The password for TCP MD5 Signature is too long");
178 
179     md5.tcpm_keylen = len;
180     memcpy(&md5.tcpm_key, passwd, len);
181   }
182 
183   if (pxlen < 0)
184   {
185     if (setsockopt(s->fd, SOL_TCP, TCP_MD5SIG, &md5, sizeof(md5)) < 0)
186       if (errno == ENOPROTOOPT)
187 	ERR_MSG("Kernel does not support TCP MD5 signatures");
188       else
189 	ERR("TCP_MD5SIG");
190   }
191   else
192   {
193     md5.tcpm_flags = TCP_MD5SIG_FLAG_PREFIX;
194     md5.tcpm_prefixlen = pxlen;
195 
196     if (setsockopt(s->fd, SOL_TCP, TCP_MD5SIG_EXT, &md5, sizeof(md5)) < 0)
197     {
198       if (errno == ENOPROTOOPT)
199 	ERR_MSG("Kernel does not support extended TCP MD5 signatures");
200       else
201 	ERR("TCP_MD5SIG_EXT");
202     }
203   }
204 
205   return 0;
206 }
207 
208 static inline int
sk_set_min_ttl4(sock * s,int ttl)209 sk_set_min_ttl4(sock *s, int ttl)
210 {
211   if (setsockopt(s->fd, SOL_IP, IP_MINTTL, &ttl, sizeof(ttl)) < 0)
212   {
213     if (errno == ENOPROTOOPT)
214       ERR_MSG("Kernel does not support IPv4 TTL security");
215     else
216       ERR("IP_MINTTL");
217   }
218 
219   return 0;
220 }
221 
222 static inline int
sk_set_min_ttl6(sock * s,int ttl)223 sk_set_min_ttl6(sock *s, int ttl)
224 {
225   if (setsockopt(s->fd, SOL_IPV6, IPV6_MINHOPCOUNT, &ttl, sizeof(ttl)) < 0)
226   {
227     if (errno == ENOPROTOOPT)
228       ERR_MSG("Kernel does not support IPv6 TTL security");
229     else
230       ERR("IPV6_MINHOPCOUNT");
231   }
232 
233   return 0;
234 }
235 
236 static inline int
sk_disable_mtu_disc4(sock * s)237 sk_disable_mtu_disc4(sock *s)
238 {
239   int dont = IP_PMTUDISC_DONT;
240 
241   if (setsockopt(s->fd, SOL_IP, IP_MTU_DISCOVER, &dont, sizeof(dont)) < 0)
242     ERR("IP_MTU_DISCOVER");
243 
244   return 0;
245 }
246 
247 static inline int
sk_disable_mtu_disc6(sock * s)248 sk_disable_mtu_disc6(sock *s)
249 {
250   int dont = IPV6_PMTUDISC_DONT;
251 
252   if (setsockopt(s->fd, SOL_IPV6, IPV6_MTU_DISCOVER, &dont, sizeof(dont)) < 0)
253     ERR("IPV6_MTU_DISCOVER");
254 
255   return 0;
256 }
257 
258 int sk_priority_control = 7;
259 
260 static inline int
sk_set_priority(sock * s,int prio)261 sk_set_priority(sock *s, int prio)
262 {
263   if (setsockopt(s->fd, SOL_SOCKET, SO_PRIORITY, &prio, sizeof(prio)) < 0)
264     ERR("SO_PRIORITY");
265 
266   return 0;
267 }
268 
269