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