GNU/Hurd support
[ctdb.git] / common / system_gnu.c
1 /* 
2    ctdb system specific code to manage raw sockets on linux
3
4    Copyright (C) Ronnie Sahlberg  2007
5    Copyright (C) Andrew Tridgell  2007
6    Copyright (C) Marc Dequènes (Duck) 2009
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, see <http://www.gnu.org/licenses/>.
20
21
22   This file is a copy of 'common/system_linux.c' adapted for Hurd needs,
23   and inspired by 'common/system_aix.c' for the pcap usage.
24 */
25
26 #include "includes.h"
27 #include "system/network.h"
28 #include "system/filesys.h"
29 #include "system/wait.h"
30 #include "../include/ctdb_private.h"
31 #include "lib/tevent/tevent.h"
32 #include <net/ethernet.h>
33 #include <netinet/ip6.h>
34 #include <net/if_arp.h>
35 #include <pcap.h>
36
37
38 #ifndef ETHERTYPE_IP6
39 #define ETHERTYPE_IP6 0x86dd
40 #endif
41
42 /*
43   calculate the tcp checksum for tcp over ipv6
44 */
45 static uint16_t tcp_checksum6(uint16_t *data, size_t n, struct ip6_hdr *ip6)
46 {
47         uint32_t phdr[2];
48         uint32_t sum = 0;
49         uint16_t sum2;
50
51         sum += uint16_checksum((uint16_t *)(void *)&ip6->ip6_src, 16);
52         sum += uint16_checksum((uint16_t *)(void *)&ip6->ip6_dst, 16);
53
54         phdr[0] = htonl(n);
55         phdr[1] = htonl(ip6->ip6_nxt);
56         sum += uint16_checksum((uint16_t *)phdr, 8);
57
58         sum += uint16_checksum(data, n);
59
60         sum = (sum & 0xFFFF) + (sum >> 16);
61         sum = (sum & 0xFFFF) + (sum >> 16);
62         sum2 = htons(sum);
63         sum2 = ~sum2;
64         if (sum2 == 0) {
65                 return 0xFFFF;
66         }
67         return sum2;
68 }
69
70 /*
71   send gratuitous arp reply after we have taken over an ip address
72
73   saddr is the address we are trying to claim
74   iface is the interface name we will be using to claim the address
75  */
76 int ctdb_sys_send_arp(const ctdb_sock_addr *addr, const char *iface)
77 {
78         /* FIXME We dont do gratuitous arp on Hurd yet */
79         return 0;
80 }
81
82
83 /*
84   simple TCP checksum - assumes data is multiple of 2 bytes long
85  */
86 static uint16_t tcp_checksum(uint16_t *data, size_t n, struct iphdr *ip)
87 {
88         uint32_t sum = uint16_checksum(data, n);
89         uint16_t sum2;
90         sum += uint16_checksum((uint16_t *)(void *)&ip->saddr,
91                                sizeof(ip->saddr));
92         sum += uint16_checksum((uint16_t *)(void *)&ip->daddr,
93                                sizeof(ip->daddr));
94         sum += ip->protocol + n;
95         sum = (sum & 0xFFFF) + (sum >> 16);
96         sum = (sum & 0xFFFF) + (sum >> 16);
97         sum2 = htons(sum);
98         sum2 = ~sum2;
99         if (sum2 == 0) {
100                 return 0xFFFF;
101         }
102         return sum2;
103 }
104
105 /*
106   Send tcp segment from the specified IP/port to the specified
107   destination IP/port. 
108
109   This is used to trigger the receiving host into sending its own ACK,
110   which should trigger early detection of TCP reset by the client
111   after IP takeover
112
113   This can also be used to send RST segments (if rst is true) and also
114   if correct seq and ack numbers are provided.
115  */
116 int ctdb_sys_send_tcp(const ctdb_sock_addr *dest, 
117                       const ctdb_sock_addr *src,
118                       uint32_t seq, uint32_t ack, int rst)
119 {
120         int s;
121         int ret;
122         uint32_t one = 1;
123         uint16_t tmpport;
124         ctdb_sock_addr *tmpdest;
125         struct {
126                 struct iphdr ip;
127                 struct tcphdr tcp;
128         } ip4pkt;
129         struct {
130                 struct ip6_hdr ip6;
131                 struct tcphdr tcp;
132         } ip6pkt;
133
134         switch (src->ip.sin_family) {
135         case AF_INET:
136                 ZERO_STRUCT(ip4pkt);
137                 ip4pkt.ip.version  = 4;
138                 ip4pkt.ip.ihl      = sizeof(ip4pkt.ip)/4;
139                 ip4pkt.ip.tot_len  = htons(sizeof(ip4pkt));
140                 ip4pkt.ip.ttl      = 255;
141                 ip4pkt.ip.protocol = IPPROTO_TCP;
142                 ip4pkt.ip.saddr    = src->ip.sin_addr.s_addr;
143                 ip4pkt.ip.daddr    = dest->ip.sin_addr.s_addr;
144                 ip4pkt.ip.check    = 0;
145
146                 ip4pkt.tcp.source   = src->ip.sin_port;
147                 ip4pkt.tcp.dest     = dest->ip.sin_port;
148                 ip4pkt.tcp.seq      = seq;
149                 ip4pkt.tcp.ack_seq  = ack;
150                 ip4pkt.tcp.ack      = 1;
151                 if (rst) {
152                         ip4pkt.tcp.rst      = 1;
153                 }
154                 ip4pkt.tcp.doff     = sizeof(ip4pkt.tcp)/4;
155                 /* this makes it easier to spot in a sniffer */
156                 ip4pkt.tcp.window   = htons(1234);
157                 ip4pkt.tcp.check    = tcp_checksum((uint16_t *)&ip4pkt.tcp, sizeof(ip4pkt.tcp), &ip4pkt.ip);
158
159                 /* open a raw socket to send this segment from */
160                 s = socket(AF_INET, SOCK_RAW, htons(IPPROTO_RAW));
161                 if (s == -1) {
162                         DEBUG(DEBUG_CRIT,(__location__ " failed to open raw socket (%s)\n",
163                                  strerror(errno)));
164                         return -1;
165                 }
166
167                 ret = setsockopt(s, IPPROTO_IP, IP_HDRINCL, &one, sizeof(one));
168                 if (ret != 0) {
169                         DEBUG(DEBUG_CRIT,(__location__ " failed to setup IP headers (%s)\n",
170                                  strerror(errno)));
171                         close(s);
172                         return -1;
173                 }
174
175                 set_nonblocking(s);
176                 set_close_on_exec(s);
177
178                 ret = sendto(s, &ip4pkt, sizeof(ip4pkt), 0, &dest->ip, sizeof(dest->ip));
179                 close(s);
180                 if (ret != sizeof(ip4pkt)) {
181                         DEBUG(DEBUG_CRIT,(__location__ " failed sendto (%s)\n", strerror(errno)));
182                         return -1;
183                 }
184                 break;
185         case AF_INET6:
186                 ZERO_STRUCT(ip6pkt);
187                 ip6pkt.ip6.ip6_vfc  = 0x60;
188                 ip6pkt.ip6.ip6_plen = htons(20);
189                 ip6pkt.ip6.ip6_nxt  = IPPROTO_TCP;
190                 ip6pkt.ip6.ip6_hlim = 64;
191                 ip6pkt.ip6.ip6_src  = src->ip6.sin6_addr;
192                 ip6pkt.ip6.ip6_dst  = dest->ip6.sin6_addr;
193
194                 ip6pkt.tcp.source   = src->ip6.sin6_port;
195                 ip6pkt.tcp.dest     = dest->ip6.sin6_port;
196                 ip6pkt.tcp.seq      = seq;
197                 ip6pkt.tcp.ack_seq  = ack;
198                 ip6pkt.tcp.ack      = 1;
199                 if (rst) {
200                         ip6pkt.tcp.rst      = 1;
201                 }
202                 ip6pkt.tcp.doff     = sizeof(ip6pkt.tcp)/4;
203                 /* this makes it easier to spot in a sniffer */
204                 ip6pkt.tcp.window   = htons(1234);
205                 ip6pkt.tcp.check    = tcp_checksum6((uint16_t *)&ip6pkt.tcp, sizeof(ip6pkt.tcp), &ip6pkt.ip6);
206
207                 s = socket(PF_INET6, SOCK_RAW, IPPROTO_RAW);
208                 if (s == -1) {
209                         DEBUG(DEBUG_CRIT, (__location__ " Failed to open sending socket\n"));
210                         return -1;
211
212                 }
213                 /* sendto() dont like if the port is set and the socket is
214                    in raw mode.
215                 */
216                 tmpdest = discard_const(dest);
217                 tmpport = tmpdest->ip6.sin6_port;
218
219                 tmpdest->ip6.sin6_port = 0;
220                 ret = sendto(s, &ip6pkt, sizeof(ip6pkt), 0, &dest->ip6, sizeof(dest->ip6));
221                 tmpdest->ip6.sin6_port = tmpport;
222                 close(s);
223
224                 if (ret != sizeof(ip6pkt)) {
225                         DEBUG(DEBUG_CRIT,(__location__ " failed sendto (%s)\n", strerror(errno)));
226                         return -1;
227                 }
228                 break;
229
230         default:
231                 DEBUG(DEBUG_CRIT,(__location__ " not an ipv4/v6 address\n"));
232                 return -1;
233         }
234
235         return 0;
236 }
237
238 /* 
239    This function is used to open a raw socket to capture from
240  */
241 int ctdb_sys_open_capture_socket(const char *iface, void **private_data)
242 {
243         pcap_t *pt;
244
245         pt=pcap_open_live(iface, 100, 0, 0, NULL);
246         if (pt == NULL) {
247                 DEBUG(DEBUG_CRIT,("Failed to open capture device %s\n", iface));
248                 return -1;
249         }
250         *((pcap_t **)private_data) = pt;
251
252         return pcap_fileno(pt);
253 }
254
255 /* This function is used to close the capture socket
256  */
257 int ctdb_sys_close_capture_socket(void *private_data)
258 {
259         pcap_t *pt = (pcap_t *)private_data;
260         pcap_close(pt);
261         return 0;
262 }
263
264
265 /*
266   called when the raw socket becomes readable
267  */
268 int ctdb_sys_read_tcp_packet(int s, void *private_data, 
269                         ctdb_sock_addr *src, ctdb_sock_addr *dst,
270                         uint32_t *ack_seq, uint32_t *seq)
271 {
272         int ret;
273 #define RCVPKTSIZE 100
274         char pkt[RCVPKTSIZE];
275         struct ether_header *eth;
276         struct iphdr *ip;
277         struct ip6_hdr *ip6;
278         struct tcphdr *tcp;
279
280         ret = recv(s, pkt, RCVPKTSIZE, MSG_TRUNC);
281         if (ret < sizeof(*eth)+sizeof(*ip)) {
282                 return -1;
283         }
284
285         /* Ethernet */
286         eth = (struct ether_header *)pkt;
287
288         /* we want either IPv4 or IPv6 */
289         if (ntohs(eth->ether_type) == ETHERTYPE_IP) {
290                 /* IP */
291                 ip = (struct iphdr *)(eth+1);
292
293                 /* We only want IPv4 packets */
294                 if (ip->version != 4) {
295                         return -1;
296                 }
297                 /* Dont look at fragments */
298                 if ((ntohs(ip->frag_off)&0x1fff) != 0) {
299                         return -1;
300                 }
301                 /* we only want TCP */
302                 if (ip->protocol != IPPROTO_TCP) {
303                         return -1;
304                 }
305
306                 /* make sure its not a short packet */
307                 if (offsetof(struct tcphdr, ack_seq) + 4 + 
308                     (ip->ihl*4) + sizeof(*eth) > ret) {
309                         return -1;
310                 }
311                 /* TCP */
312                 tcp = (struct tcphdr *)((ip->ihl*4) + (char *)ip);
313
314                 /* tell the caller which one we've found */
315                 src->ip.sin_family      = AF_INET;
316                 src->ip.sin_addr.s_addr = ip->saddr;
317                 src->ip.sin_port        = tcp->source;
318                 dst->ip.sin_family      = AF_INET;
319                 dst->ip.sin_addr.s_addr = ip->daddr;
320                 dst->ip.sin_port        = tcp->dest;
321                 *ack_seq                = tcp->ack_seq;
322                 *seq                    = tcp->seq;
323
324                 return 0;
325         } else if (ntohs(eth->ether_type) == ETHERTYPE_IP6) {
326                 /* IP6 */
327                 ip6 = (struct ip6_hdr *)(eth+1);
328
329                 /* we only want TCP */
330                 if (ip6->ip6_nxt != IPPROTO_TCP) {
331                         return -1;
332                 }
333
334                 /* TCP */
335                 tcp = (struct tcphdr *)(ip6+1);
336
337                 /* tell the caller which one we've found */
338                 src->ip6.sin6_family = AF_INET6;
339                 src->ip6.sin6_port   = tcp->source;
340                 src->ip6.sin6_addr   = ip6->ip6_src;
341
342                 dst->ip6.sin6_family = AF_INET6;
343                 dst->ip6.sin6_port   = tcp->dest;
344                 dst->ip6.sin6_addr   = ip6->ip6_dst;
345
346                 *ack_seq             = tcp->ack_seq;
347                 *seq                 = tcp->seq;
348
349                 return 0;
350         }
351
352         return -1;
353 }
354
355 bool ctdb_sys_check_iface_exists(const char *iface)
356 {
357         return true;
358 }
359
360 int ctdb_get_peer_pid(const int fd, pid_t *peer_pid)
361 {
362         /* FIXME not implemented */
363         return 1;
364 }