]> git.ipfire.org Git - thirdparty/bird.git/blob - sysdep/linux/sysio.h
2fa5f0a9e893738170561113eb95676ea02e28df
[thirdparty/bird.git] / sysdep / linux / sysio.h
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 #ifdef IPV6
10
11 #ifndef IPV6_UNICAST_HOPS
12 /* Needed on glibc 2.0 systems */
13 #include <linux/in6.h>
14 #define CONFIG_IPV6_GLIBC_20
15 #endif
16
17 static inline void
18 set_inaddr(struct in6_addr *ia, ip_addr a)
19 {
20 ipa_hton(a);
21 memcpy(ia, &a, sizeof(a));
22 }
23
24 #else
25
26 #include <net/if.h>
27
28 static inline void
29 set_inaddr(struct in_addr *ia, ip_addr a)
30 {
31 ipa_hton(a);
32 memcpy(&ia->s_addr, &a, sizeof(a));
33 }
34
35 /*
36 * Multicasting in Linux systems is a real mess. Not only different kernels
37 * have different interfaces, but also different libc's export it in different
38 * ways. Horrible.
39 */
40
41 static inline char *sysio_mcast_setup(sock *s)
42 {
43 int zero = 0;
44
45 if (ipa_nonzero(s->daddr))
46 {
47 if (
48 #ifdef IP_DEFAULT_MULTICAST_TTL
49 s->ttl != IP_DEFAULT_MULTICAST_TTL &&
50 #endif
51 setsockopt(s->fd, SOL_IP, IP_MULTICAST_TTL, &s->ttl, sizeof(s->ttl)) < 0)
52 return "IP_MULTICAST_TTL";
53 if (
54 #ifdef IP_DEFAULT_MULTICAST_LOOP
55 IP_DEFAULT_MULTICAST_LOOP &&
56 #endif
57 setsockopt(s->fd, SOL_IP, IP_MULTICAST_LOOP, &zero, sizeof(zero)) < 0)
58 return "IP_MULTICAST_LOOP";
59 }
60 return NULL;
61 }
62
63 #ifdef CONFIG_LINUX_MC_MREQN
64 /*
65 * 2.1 and newer kernels use struct mreqn which passes ifindex, so no
66 * problems with unnumbered devices.
67 */
68
69 #ifndef HAVE_STRUCT_IP_MREQN
70 /* Several versions of glibc don't define this structure, so we have to do it ourselves */
71 struct ip_mreqn
72 {
73 struct in_addr imr_multiaddr; /* IP multicast address of group */
74 struct in_addr imr_address; /* local IP address of interface */
75 int imr_ifindex; /* Interface index */
76 };
77 #endif
78
79 static inline char *sysio_mcast_join(sock *s)
80 {
81 struct ip_mreqn mreq;
82 char *err;
83 struct ifreq ifr;
84
85 if (err = sysio_mcast_setup(s))
86 return err;
87 strcpy(ifr.ifr_name, s->iface->name);
88 if (setsockopt(s->fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) < 0)
89 return "SO_BINDTODEVICE";
90 mreq.imr_ifindex = s->iface->index;
91 set_inaddr(&mreq.imr_address, s->iface->addr->ip);
92 set_inaddr(&mreq.imr_multiaddr, s->daddr);
93 /* This defines where should we send _outgoing_ multicasts */
94 if (ipa_nonzero(s->daddr) && setsockopt(s->fd, SOL_IP, IP_MULTICAST_IF, &mreq, sizeof(mreq)) < 0)
95 return "IP_MULTICAST_IF";
96 /* And this one sets interface for _receiving_ multicasts from */
97 if (ipa_nonzero(s->saddr) && setsockopt(s->fd, SOL_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
98 return "IP_ADD_MEMBERSHIP";
99 return NULL;
100 }
101 #endif
102
103 #if defined(CONFIG_LINUX_MC_MREQ) || defined(CONFIG_LINUX_MC_MREQ_BIND)
104 /*
105 * Older kernels support only struct mreq which matches interfaces by their
106 * addresses and thus fails on unnumbered devices. On newer 2.0 kernels
107 * we can use SO_BINDTODEVICE to circumvent this problem.
108 */
109
110 static inline char *sysio_mcast_join(sock *s)
111 {
112 struct in_addr mreq;
113 struct ip_mreq mreq_add;
114 char *err;
115
116 if (err = sysio_mcast_setup(s))
117 return err;
118 set_inaddr(&mreq, s->iface->addr->ip);
119 #ifdef CONFIG_LINUX_MC_MREQ_BIND
120 {
121 struct ifreq ifr;
122 strcpy(ifr.ifr_name, s->iface->name);
123 if (setsockopt(s->fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) < 0)
124 return "SO_BINDTODEVICE";
125 mreq_add.imr_interface.s_addr = INADDR_ANY;
126 }
127 #else
128 mreq_add.imr_interface = mreq;
129 #endif
130 set_inaddr(&mreq_add.imr_multiaddr, s->daddr);
131 /* This defines where should we send _outgoing_ multicasts */
132 if (ipa_nonzero(s->daddr) && setsockopt(s->fd, SOL_IP, IP_MULTICAST_IF, &mreq, sizeof(mreq)) < 0)
133 return "IP_MULTICAST_IF";
134 /* And this one sets interface for _receiving_ multicasts from */
135 if (ipa_nonzero(s->saddr) && setsockopt(s->fd, SOL_IP, IP_ADD_MEMBERSHIP, &mreq_add, sizeof(mreq_add)) < 0)
136 return "IP_ADD_MEMBERSHIP";
137 return NULL;
138 }
139 #endif
140
141 #endif
142
143 #include <linux/socket.h>
144 #include <linux/tcp.h>
145
146 /* For the case that we have older kernel headers */
147 /* Copied from Linux kernel file include/linux/tcp.h */
148
149 #ifndef TCP_MD5SIG
150
151 #define TCP_MD5SIG 14
152 #define TCP_MD5SIG_MAXKEYLEN 80
153
154 struct tcp_md5sig {
155 struct sockaddr_storage tcpm_addr; /* address associated */
156 __u16 __tcpm_pad1; /* zero */
157 __u16 tcpm_keylen; /* key length */
158 __u32 __tcpm_pad2; /* zero */
159 __u8 tcpm_key[TCP_MD5SIG_MAXKEYLEN]; /* key (binary) */
160 };
161
162 #endif
163
164 static int
165 sk_set_md5_auth_int(sock *s, sockaddr *sa, char *passwd)
166 {
167 struct tcp_md5sig md5;
168
169 memset(&md5, 0, sizeof(md5));
170 memcpy(&md5.tcpm_addr, (struct sockaddr *) sa, sizeof(*sa));
171
172 if (passwd)
173 {
174 int len = strlen(passwd);
175
176 if (len > TCP_MD5SIG_MAXKEYLEN)
177 {
178 log(L_ERR "MD5 password too long");
179 return -1;
180 }
181
182 md5.tcpm_keylen = len;
183 memcpy(&md5.tcpm_key, passwd, len);
184 }
185
186 int rv = setsockopt(s->fd, IPPROTO_TCP, TCP_MD5SIG, &md5, sizeof(md5));
187
188 if (rv < 0)
189 {
190 if (errno == ENOPROTOOPT)
191 log(L_ERR "Kernel does not support TCP MD5 signatures");
192 else
193 log(L_ERR "sk_set_md5_auth_int: setsockopt: %m");
194 }
195
196 return rv;
197 }