Merge git://git.samba.org/tridge/ctdb
[ambi/samba-autobuild/.git] / ctdb / common / system_aix.c
1 /* 
2    ctdb system specific code to manage raw sockets on aix
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
22 #include "includes.h"
23 #include "system/network.h"
24 #include "system/filesys.h"
25 #include "system/wait.h"
26 #include <netinet/if_ether.h>
27 #include "../include/ctdb_private.h"
28 #include <sys/ndd_var.h>
29 #include <sys/kinfo.h>
30 #include <pcap.h>
31
32
33
34 #if 0
35 This function is no longer used and its code should be moved into
36 send tcp packet   after that function has been enhanced to do ipv6 as well.
37
38 /* This function is used to open a raw socket to send tickles from
39  */
40 int ctdb_sys_open_sending_socket(void)
41 {
42         int s, ret;
43         uint32_t one = 1;
44
45         s = socket(AF_INET, SOCK_RAW, htons(IPPROTO_RAW));
46         if (s == -1) {
47                 DEBUG(DEBUG_CRIT,(" failed to open raw socket (%s)\n",
48                          strerror(errno)));
49                 return -1;
50         }
51
52         ret = setsockopt(s, IPPROTO_IP, IP_HDRINCL, &one, sizeof(one));
53         if (ret != 0) {
54                 DEBUG(DEBUG_CRIT, (" failed to setup IP headers (%s)\n",
55                          strerror(errno)));
56                 close(s);
57                 return -1;
58         }
59
60         set_nonblocking(s);
61         set_close_on_exec(s);
62
63         return s;
64 }
65 #endif
66
67 /*
68   uint16 checksum for n bytes
69  */
70 static uint32_t uint16_checksum(uint16_t *data, size_t n)
71 {
72         uint32_t sum=0;
73         while (n>=2) {
74                 sum += (uint32_t)ntohs(*data);
75                 data++;
76                 n -= 2;
77         }
78         if (n == 1) {
79                 sum += (uint32_t)ntohs(*(uint8_t *)data);
80         }
81         return sum;
82 }
83
84 /*
85   simple TCP checksum - assumes data is multiple of 2 bytes long
86  */
87 static uint16_t tcp_checksum(uint16_t *data, size_t n, struct ip *ip)
88 {
89         uint32_t sum = uint16_checksum(data, n);
90         uint16_t sum2;
91
92         sum += uint16_checksum((uint16_t *)&ip->ip_src, sizeof(ip->ip_src));
93         sum += uint16_checksum((uint16_t *)&ip->ip_dst, sizeof(ip->ip_dst));
94         sum += ip->ip_p + 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(int s,
117                       const struct sockaddr_in *dest, 
118                       const struct sockaddr_in *src,
119                       uint32_t seq, uint32_t ack, int rst)
120 {
121         int ret;
122         struct {
123                 struct ip ip;
124                 struct tcphdr tcp;
125         } pkt;
126
127         /* for now, we only handle AF_INET addresses */
128         if (src->sin_family != AF_INET || dest->sin_family != AF_INET) {
129                 DEBUG(DEBUG_CRIT,(__location__ " not an ipv4 address\n"));
130                 return -1;
131         }
132
133         memset(&pkt, 0, sizeof(pkt));
134         pkt.ip.ip_v     = 4;
135         pkt.ip.ip_hl    = sizeof(pkt.ip)/4;
136         pkt.ip.ip_len   = htons(sizeof(pkt));
137         pkt.ip.ip_ttl   = 255;
138         pkt.ip.ip_p     = IPPROTO_TCP;
139         pkt.ip.ip_src.s_addr   = src->sin_addr.s_addr;
140         pkt.ip.ip_dst.s_addr   = dest->sin_addr.s_addr;
141         pkt.ip.ip_sum   = 0;
142
143         pkt.tcp.th_sport   = src->sin_port;
144         pkt.tcp.th_dport     = dest->sin_port;
145         pkt.tcp.th_seq      = seq;
146         pkt.tcp.th_ack    = ack;
147         pkt.tcp.th_flags  = TH_ACK;
148         if (rst) {
149                 pkt.tcp.th_flags      = TH_RST;
150         }
151         pkt.tcp.th_off    = sizeof(pkt.tcp)/4;
152         pkt.tcp.th_win   = htons(1234);
153         pkt.tcp.th_sum    = tcp_checksum((uint16_t *)&pkt.tcp, sizeof(pkt.tcp), &pkt.ip);
154
155         ret = sendto(s, &pkt, sizeof(pkt), 0, (struct sockaddr *)dest, sizeof(*dest));
156         if (ret != sizeof(pkt)) {
157                 DEBUG(DEBUG_CRIT,(__location__ " failed sendto (%s)\n", strerror(errno)));
158                 return -1;
159         }
160
161         return 0;
162 }
163
164
165 /*
166   see if we currently have an interface with the given IP
167
168   we try to bind to it, and if that fails then we don't have that IP
169   on an interface
170  */
171 bool ctdb_sys_have_ip(struct sockaddr_in ip)
172 {
173         int s;
174         int ret;
175         
176         ip.sin_port = 0;
177         s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
178         if (s == -1) {
179                 return false;
180         }
181         ret = bind(s, (struct sockaddr *)&ip, sizeof(ip));
182         close(s);
183         return ret == 0;
184 }
185
186
187
188
189 /* This function is used to open a raw socket to capture from
190  */
191 int ctdb_sys_open_capture_socket(const char *iface, void **private_data)
192 {
193         pcap_t *pt;
194
195         pt=pcap_open_live(iface, 100, 0, 0, NULL);
196         if (pt == NULL) {
197                 DEBUG(DEBUG_CRIT,("Failed to open capture device %s\n", iface));
198                 return -1;
199         }
200         *((pcap_t **)private_data) = pt;
201
202         return pcap_fileno(pt);
203 }
204
205
206 /* This function is used to close the capture socket
207  */
208 int ctdb_sys_close_capture_socket(void *private_data)
209 {
210         pcap_t *pt = (pcap_t *)private_data;
211         pcap_close(pt);
212         return 0;
213 }
214
215
216
217 /*
218   send gratuitous arp reply after we have taken over an ip address
219
220   saddr is the address we are trying to claim
221   iface is the interface name we will be using to claim the address
222  */
223 int ctdb_sys_send_arp(const struct sockaddr_in *saddr, const char *iface)
224 {
225         /* We dont do grat arp on aix yet */
226         return 0;
227 }
228
229
230
231 /*
232   get ethernet MAC address on AIX
233  */
234 static int aix_get_mac_addr(const char *device_name, uint8_t mac[6])
235 {
236         size_t ksize;
237         struct kinfo_ndd *ndd;
238         int count, i;
239
240         ksize = getkerninfo(KINFO_NDD, 0, 0, 0);
241         if (ksize == 0) {
242                 errno = ENOSYS;
243                 return -1;
244         }
245
246         ndd = (struct kinfo_ndd *)malloc(ksize);
247         if (ndd == NULL) {
248                 errno = ENOMEM;
249                 return -1;
250         }
251
252         if (getkerninfo(KINFO_NDD, ndd, &ksize, 0) == -1) {
253                 errno = ENOSYS;
254                 return -1;
255         }
256
257         count= ksize/sizeof(struct kinfo_ndd);
258         for (i=0;i<count;i++) {
259                 if ( (ndd[i].ndd_type != NDD_ETHER) 
260                 &&   (ndd[i].ndd_type != NDD_ISO88023) ) {
261                         continue;
262                 }
263                 if (ndd[i].ndd_addrlen != 6) {
264                         continue;
265                 }
266                 if (!(ndd[i].ndd_flags&NDD_UP)) {
267                         continue;
268                 }
269                 if ( strcmp(device_name, ndd[i].ndd_name)
270                 &&   strcmp(device_name, ndd[i].ndd_alias) ) {
271                         continue;
272                 }
273                 memcpy(mac, ndd[i].ndd_addr, 6);
274                 free(ndd);
275                 return 0;
276         }
277         free(ndd);
278         errno = ENOENT;
279         return -1;
280 }
281
282 int ctdb_sys_read_tcp_packet(int s, void *private_data, 
283                         struct sockaddr_in *src, struct sockaddr_in *dst,
284                         uint32_t *ack_seq, uint32_t *seq)
285 {
286         int ret;
287         struct ether_header *eth;
288         struct ip *ip;
289         struct tcphdr *tcp;
290         struct ctdb_killtcp_connection *conn;
291         struct pcap_pkthdr pkthdr;
292         const u_char *buffer;
293         pcap_t *pt = (pcap_t *)private_data;
294
295         buffer=pcap_next(pt, &pkthdr);
296         if (buffer==NULL) {
297                 return -1;
298         }
299
300         /* Ethernet */
301         eth = (struct ether_header *)buffer;
302
303         /* We are only interested in IP packets */
304         if (eth->ether_type != htons(ETHERTYPE_IP)) {
305                 return -1;
306         }
307
308         /* IP */
309         ip = (struct ip *)(eth+1);
310
311         /* We only want IPv4 packets */
312         if (ip->ip_v != 4) {
313                 return -1;
314         }
315         /* Dont look at fragments */
316         if ((ntohs(ip->ip_off)&0x1fff) != 0) {
317                 return -1;
318         }
319         /* we only want TCP */
320         if (ip->ip_p != IPPROTO_TCP) {
321                 return -1;
322         }
323
324         /* make sure its not a short packet */
325         if (offsetof(struct tcphdr, th_ack) + 4 + 
326             (ip->ip_hl*4) > ret) {
327                 return -1;
328         }
329         /* TCP */
330         tcp = (struct tcphdr *)((ip->ip_hl*4) + (char *)ip);
331         
332         /* tell the caller which one we've found */
333         src->sin_addr.s_addr = ip->ip_src.s_addr;
334         src->sin_port        = tcp->th_sport;
335         dst->sin_addr.s_addr = ip->ip_dst.s_addr;
336         dst->sin_port        = tcp->th_dport;
337         *ack_seq             = tcp->th_ack;
338         *seq                 = tcp->th_seq;
339
340         return 0;
341 }
342
343
344