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