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