add some support for controlling Linux or AIX in the makefile
[ambi/samba-autobuild/.git] / ctdb / common / system_linux.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
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "includes.h"
22 #include "system/network.h"
23 #include "system/filesys.h"
24 #include "system/wait.h"
25 #include "../include/ctdb_private.h"
26 #include "lib/events/events.h"
27 #include <netinet/if_ether.h>
28 #include <net/if_arp.h>
29
30
31
32 /*
33   send gratuitous arp reply after we have taken over an ip address
34
35   saddr is the address we are trying to claim
36   iface is the interface name we will be using to claim the address
37  */
38 int ctdb_sys_send_arp(const struct sockaddr_in *saddr, const char *iface)
39 {
40         int s, ret;
41         struct sockaddr sa;
42         struct ether_header *eh;
43         struct arphdr *ah;
44         struct ifreq if_hwaddr;
45         unsigned char buffer[64]; /*minimum eth frame size */
46         char *ptr;
47
48         /* for now, we only handle AF_INET addresses */
49         if (saddr->sin_family != AF_INET) {
50                 DEBUG(0,(__location__ " not an ipv4 address (family is %u)\n", saddr->sin_family));
51                 return -1;
52         }
53
54         s = socket(AF_INET, SOCK_PACKET, htons(ETHERTYPE_ARP));
55         if (s == -1){
56                 DEBUG(0,(__location__ " failed to open raw socket\n"));
57                 return -1;
58         }
59
60         /* get the mac address */
61         strcpy(if_hwaddr.ifr_name, iface);
62         ret = ioctl(s, SIOCGIFHWADDR, &if_hwaddr);
63         if ( ret < 0 ) {
64                 close(s);
65                 DEBUG(0,(__location__ " ioctl failed\n"));
66                 return -1;
67         }
68         if (ARPHRD_LOOPBACK == if_hwaddr.ifr_hwaddr.sa_family) {
69                 DEBUG(3,("Ignoring loopback arp request\n"));
70                 close(s);
71                 return 0;
72         }
73         if (if_hwaddr.ifr_hwaddr.sa_family != AF_LOCAL) {
74                 close(s);
75                 errno = EINVAL;
76                 DEBUG(0,(__location__ " not an ethernet address family (0x%x)\n",
77                          if_hwaddr.ifr_hwaddr.sa_family));
78                 return -1;
79         }
80
81
82         memset(buffer, 0 , 64);
83         eh = (struct ether_header *)buffer;
84         memset(eh->ether_dhost, 0xff, ETH_ALEN);
85         memcpy(eh->ether_shost, if_hwaddr.ifr_hwaddr.sa_data, ETH_ALEN);
86         eh->ether_type = htons(ETHERTYPE_ARP);
87
88         ah = (struct arphdr *)&buffer[sizeof(struct ether_header)];
89         ah->ar_hrd = htons(ARPHRD_ETHER);
90         ah->ar_pro = htons(ETH_P_IP);
91         ah->ar_hln = ETH_ALEN;
92         ah->ar_pln = 4;
93
94         /* send a gratious arp */
95         ah->ar_op  = htons(ARPOP_REQUEST);
96         ptr = (char *)&ah[1];
97         memcpy(ptr, if_hwaddr.ifr_hwaddr.sa_data, ETH_ALEN);
98         ptr+=ETH_ALEN;
99         memcpy(ptr, &saddr->sin_addr, 4);         
100         ptr+=4;
101         memset(ptr, 0, ETH_ALEN); 
102         ptr+=ETH_ALEN;
103         memcpy(ptr, &saddr->sin_addr, 4);         
104         ptr+=4;
105
106         strncpy(sa.sa_data, iface, sizeof(sa.sa_data));
107         ret = sendto(s, buffer, 64, 0, &sa, sizeof(sa));
108         if (ret < 0 ){
109                 close(s);
110                 DEBUG(0,(__location__ " failed sendto\n"));
111                 return -1;
112         }
113
114         /* send unsolicited arp reply broadcast */
115         ah->ar_op  = htons(ARPOP_REPLY);
116         ptr = (char *)&ah[1];
117         memcpy(ptr, if_hwaddr.ifr_hwaddr.sa_data, ETH_ALEN);
118         ptr+=ETH_ALEN;
119         memcpy(ptr, &saddr->sin_addr, 4);         
120         ptr+=4;
121         memcpy(ptr, if_hwaddr.ifr_hwaddr.sa_data, ETH_ALEN);
122         ptr+=ETH_ALEN;
123         memcpy(ptr, &saddr->sin_addr, 4);         
124         ptr+=4;
125
126         strncpy(sa.sa_data, iface, sizeof(sa.sa_data));
127         ret = sendto(s, buffer, 64, 0, &sa, sizeof(sa));
128         if (ret < 0 ){
129                 DEBUG(0,(__location__ " failed sendto\n"));
130                 return -1;
131         }
132
133         close(s);
134         return 0;
135 }
136
137
138 /*
139   uint16 checksum for n bytes
140  */
141 static uint32_t uint16_checksum(uint16_t *data, size_t n)
142 {
143         uint32_t sum=0;
144         while (n>=2) {
145                 sum += (uint32_t)ntohs(*data);
146                 data++;
147                 n -= 2;
148         }
149         if (n == 1) {
150                 sum += (uint32_t)ntohs(*(uint8_t *)data);
151         }
152         return sum;
153 }
154
155 /*
156   simple TCP checksum - assumes data is multiple of 2 bytes long
157  */
158 static uint16_t tcp_checksum(uint16_t *data, size_t n, struct iphdr *ip)
159 {
160         uint32_t sum = uint16_checksum(data, n);
161         uint16_t sum2;
162         sum += uint16_checksum((uint16_t *)&ip->saddr, sizeof(ip->saddr));
163         sum += uint16_checksum((uint16_t *)&ip->daddr, sizeof(ip->daddr));
164         sum += ip->protocol + n;
165         sum = (sum & 0xFFFF) + (sum >> 16);
166         sum = (sum & 0xFFFF) + (sum >> 16);
167         sum2 = htons(sum);
168         sum2 = ~sum2;
169         if (sum2 == 0) {
170                 return 0xFFFF;
171         }
172         return sum2;
173 }
174
175 /*
176   Send tcp segment from the specified IP/port to the specified
177   destination IP/port. 
178
179   This is used to trigger the receiving host into sending its own ACK,
180   which should trigger early detection of TCP reset by the client
181   after IP takeover
182
183   This can also be used to send RST segments (if rst is true) and also
184   if correct seq and ack numbers are provided.
185  */
186 int ctdb_sys_send_tcp(int s,
187                       const struct sockaddr_in *dest, 
188                       const struct sockaddr_in *src,
189                       uint32_t seq, uint32_t ack, int rst)
190 {
191         int ret;
192         struct {
193                 struct iphdr ip;
194                 struct tcphdr tcp;
195         } pkt;
196
197         /* for now, we only handle AF_INET addresses */
198         if (src->sin_family != AF_INET || dest->sin_family != AF_INET) {
199                 DEBUG(0,(__location__ " not an ipv4 address\n"));
200                 return -1;
201         }
202
203         ZERO_STRUCT(pkt);
204         pkt.ip.version  = 4;
205         pkt.ip.ihl      = sizeof(pkt.ip)/4;
206         pkt.ip.tot_len  = htons(sizeof(pkt));
207         pkt.ip.ttl      = 255;
208         pkt.ip.protocol = IPPROTO_TCP;
209         pkt.ip.saddr    = src->sin_addr.s_addr;
210         pkt.ip.daddr    = dest->sin_addr.s_addr;
211         pkt.ip.check    = 0;
212
213         pkt.tcp.source   = src->sin_port;
214         pkt.tcp.dest     = dest->sin_port;
215         pkt.tcp.seq      = seq;
216         pkt.tcp.ack_seq  = ack;
217         pkt.tcp.ack      = 1;
218         if (rst) {
219                 pkt.tcp.rst      = 1;
220         }
221         pkt.tcp.doff     = sizeof(pkt.tcp)/4;
222         pkt.tcp.window   = htons(1234); /* this makes it easier to spot in a sniffer */
223         pkt.tcp.check    = tcp_checksum((uint16_t *)&pkt.tcp, sizeof(pkt.tcp), &pkt.ip);
224
225         ret = sendto(s, &pkt, sizeof(pkt), 0, dest, sizeof(*dest));
226         if (ret != sizeof(pkt)) {
227                 DEBUG(0,(__location__ " failed sendto (%s)\n", strerror(errno)));
228                 return -1;
229         }
230
231         return 0;
232 }
233
234
235 /*
236   see if we currently have an interface with the given IP
237
238   we try to bind to it, and if that fails then we don't have that IP
239   on an interface
240  */
241 bool ctdb_sys_have_ip(const char *ip)
242 {
243         struct sockaddr_in sin;
244         int s;
245         int ret;
246
247         sin.sin_port = 0;
248         inet_aton(ip, &sin.sin_addr);
249         sin.sin_family = AF_INET;
250         s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
251         if (s == -1) {
252                 return false;
253         }
254         ret = bind(s, (struct sockaddr *)&sin, sizeof(sin));
255         close(s);
256         return ret == 0;
257 }
258
259 /* 
260    This function is used to open a raw socket to capture from
261  */
262 int ctdb_sys_open_capture_socket(const char *iface, void **private_data)
263 {
264         int s;
265
266         /* Open a socket to capture all traffic */
267         s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
268         if (s == -1) {
269                 DEBUG(0,(__location__ " failed to open raw socket\n"));
270                 return -1;
271         }
272
273         set_nonblocking(s);
274         set_close_on_exec(s);
275
276         return s;
277 }
278
279 /* 
280    This function is used to do any additional cleanup required when closing
281    a capture socket.
282    Note that the socket itself is closed automatically in the caller.
283  */
284 int ctdb_sys_close_capture_socket(void *private_data)
285 {
286         return 0;
287 }
288
289 /* 
290    This function is used to open a raw socket to send tickles from
291  */
292 int ctdb_sys_open_sending_socket(void)
293 {
294         int s, ret;
295         uint32_t one = 1;
296
297         s = socket(AF_INET, SOCK_RAW, htons(IPPROTO_RAW));
298         if (s == -1) {
299                 DEBUG(0,(__location__ " failed to open raw socket (%s)\n",
300                          strerror(errno)));
301                 return -1;
302         }
303
304         ret = setsockopt(s, SOL_IP, IP_HDRINCL, &one, sizeof(one));
305         if (ret != 0) {
306                 DEBUG(0,(__location__ " failed to setup IP headers (%s)\n",
307                          strerror(errno)));
308                 close(s);
309                 return -1;
310         }
311
312         set_nonblocking(s);
313         set_close_on_exec(s);
314
315         return s;
316 }
317
318 /*
319   called when the raw socket becomes readable
320  */
321 int ctdb_sys_read_tcp_packet(int s, void *private_data, 
322                         struct sockaddr_in *src, struct sockaddr_in *dst,
323                         uint32_t *ack_seq, uint32_t *seq)
324 {
325         int ret;
326 #define RCVPKTSIZE 100
327         char pkt[RCVPKTSIZE];
328         struct ether_header *eth;
329         struct iphdr *ip;
330         struct tcphdr *tcp;
331
332         ret = recv(s, pkt, RCVPKTSIZE, MSG_TRUNC);
333         if (ret < sizeof(*eth)+sizeof(*ip)) {
334                 return -1;
335         }
336
337         /* Ethernet */
338         eth = (struct ether_header *)pkt;
339
340         /* We only want IP packets */
341         if (ntohs(eth->ether_type) != ETHERTYPE_IP) {
342                 return -1;
343         }
344         
345         /* IP */
346         ip = (struct iphdr *)(eth+1);
347
348         /* We only want IPv4 packets */
349         if (ip->version != 4) {
350                 return -1;
351         }
352         /* Dont look at fragments */
353         if ((ntohs(ip->frag_off)&0x1fff) != 0) {
354                 return -1;
355         }
356         /* we only want TCP */
357         if (ip->protocol != IPPROTO_TCP) {
358                 return -1;
359         }
360
361         /* make sure its not a short packet */
362         if (offsetof(struct tcphdr, ack_seq) + 4 + 
363             (ip->ihl*4) + sizeof(*eth) > ret) {
364                 return -1;
365         }
366
367         /* TCP */
368         tcp = (struct tcphdr *)((ip->ihl*4) + (char *)ip);
369
370         /* tell the caller which one we've found */
371         src->sin_addr.s_addr = ip->saddr;
372         src->sin_port        = tcp->source;
373         dst->sin_addr.s_addr = ip->daddr;
374         dst->sin_port        = tcp->dest;
375         *ack_seq             = tcp->ack_seq;
376         *seq                 = tcp->seq;
377
378         return 0;
379 }
380
381