From C Cowan.
[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 "../include/ctdb_private.h"
27 #include <netinet/if_ether.h>
28 #include <netinet/ip6.h>
29 #include <net/if_arp.h>
30 #include <sys/ndd_var.h>
31 #include <sys/kinfo.h>
32 #include <pcap.h>
33
34
35
36 #if 0
37 This function is no longer used and its code should be moved into
38 send tcp packet   after that function has been enhanced to do ipv6 as well.
39
40 /* This function is used to open a raw socket to send tickles from
41  */
42 int ctdb_sys_open_sending_socket(void)
43 {
44         int s, ret;
45         uint32_t one = 1;
46
47         s = socket(AF_INET, SOCK_RAW, htons(IPPROTO_RAW));
48         if (s == -1) {
49                 DEBUG(DEBUG_CRIT,(" failed to open raw socket (%s)\n",
50                          strerror(errno)));
51                 return -1;
52         }
53
54         ret = setsockopt(s, IPPROTO_IP, IP_HDRINCL, &one, sizeof(one));
55         if (ret != 0) {
56                 DEBUG(DEBUG_CRIT, (" failed to setup IP headers (%s)\n",
57                          strerror(errno)));
58                 close(s);
59                 return -1;
60         }
61
62         set_nonblocking(s);
63         set_close_on_exec(s);
64
65         return s;
66 }
67 #endif
68
69 /*
70   uint16 checksum for n bytes
71  */
72 static uint32_t uint16_checksum(uint16_t *data, size_t n)
73 {
74         uint32_t sum=0;
75         while (n>=2) {
76                 sum += (uint32_t)ntohs(*data);
77                 data++;
78                 n -= 2;
79         }
80         if (n == 1) {
81                 sum += (uint32_t)ntohs(*(uint8_t *)data);
82         }
83         return sum;
84 }
85
86 /*
87   simple TCP checksum - assumes data is multiple of 2 bytes long
88  */
89 static uint16_t tcp_checksum(uint16_t *data, size_t n, struct ip *ip)
90 {
91         uint32_t sum = uint16_checksum(data, n);
92         uint16_t sum2;
93
94         sum += uint16_checksum((uint16_t *)&ip->ip_src, sizeof(ip->ip_src));
95         sum += uint16_checksum((uint16_t *)&ip->ip_dst, sizeof(ip->ip_dst));
96         sum += ip->ip_p + n;
97         sum = (sum & 0xFFFF) + (sum >> 16);
98         sum = (sum & 0xFFFF) + (sum >> 16);
99         sum2 = htons(sum);
100         sum2 = ~sum2;
101         if (sum2 == 0) {
102                 return 0xFFFF;
103         }
104         return sum2;
105 }
106
107 /*
108   Send tcp segment from the specified IP/port to the specified
109   destination IP/port. 
110
111   This is used to trigger the receiving host into sending its own ACK,
112   which should trigger early detection of TCP reset by the client
113   after IP takeover
114
115   This can also be used to send RST segments (if rst is true) and also
116   if correct seq and ack numbers are provided.
117  */
118 int ctdb_sys_send_tcp(const ctdb_sock_addr *dest,
119                       const ctdb_sock_addr *src,
120                       uint32_t seq, uint32_t ack, int rst)
121 {
122         int s;
123         int ret;
124         uint32_t one = 1;
125         ctdb_sock_addr *tmpdest;
126         
127         struct {
128                 struct ip ip;
129                 struct tcphdr tcp;
130         } ip4pkt;
131
132
133         /* for now, we only handle AF_INET addresses */
134         if (src->ip.sin_family != AF_INET || dest->ip.sin_family != AF_INET) {
135                 DEBUG(DEBUG_CRIT,(__location__ " not an ipv4 address\n"));
136                 return -1;
137         }
138
139
140
141         s = socket(AF_INET, SOCK_RAW, htons(IPPROTO_RAW));
142         if (s == -1) {
143                 DEBUG(DEBUG_CRIT,(" failed to open raw socket (%s)\n",
144                          strerror(errno)));
145                 return -1;
146         }
147
148         ret = setsockopt(s, IPPROTO_IP, IP_HDRINCL, &one, sizeof(one));
149         if (ret != 0) {
150                 DEBUG(DEBUG_CRIT, (" failed to setup IP headers (%s)\n",
151                          strerror(errno)));
152                 close(s);
153                 return -1;
154         }
155
156         set_nonblocking(s);
157         set_close_on_exec(s);
158
159         memset(&ip4pkt, 0, sizeof(ip4pkt));
160         ip4pkt.ip.ip_v     = 4;
161         ip4pkt.ip.ip_hl    = sizeof(ip4pkt.ip)/4;
162         ip4pkt.ip.ip_len   = htons(sizeof(ip4pkt));
163         ip4pkt.ip.ip_ttl   = 255;
164         ip4pkt.ip.ip_p     = IPPROTO_TCP;
165         ip4pkt.ip.ip_src.s_addr   = src->ip.sin_addr.s_addr;
166         ip4pkt.ip.ip_dst.s_addr   = dest->ip.sin_addr.s_addr;
167         ip4pkt.ip.ip_sum   = 0;
168
169         ip4pkt.tcp.th_sport   = src->ip.sin_port;
170         ip4pkt.tcp.th_dport     = dest->ip.sin_port;
171         ip4pkt.tcp.th_seq      = seq;
172         ip4pkt.tcp.th_ack    = ack;
173         ip4pkt.tcp.th_flags  = TH_ACK;
174         if (rst) {
175                 ip4pkt.tcp.th_flags      = TH_RST;
176         }
177         ip4pkt.tcp.th_off    = sizeof(ip4pkt.tcp)/4;
178         ip4pkt.tcp.th_win   = htons(1234);
179         ip4pkt.tcp.th_sum    = tcp_checksum((uint16_t *)&ip4pkt.tcp, sizeof(ip4pkt.tcp), &ip4pkt.ip);
180
181         ret = sendto(s, &ip4pkt, sizeof(ip4pkt), 0, (struct sockaddr *)dest, sizeof(*dest));
182         if (ret != sizeof(ip4pkt)) {
183                 DEBUG(DEBUG_CRIT,(__location__ " failed sendto (%s)\n", strerror(errno)));
184                 return -1;
185         }
186
187         return 0;
188 }
189
190
191 /*
192   see if we currently have an interface with the given IP
193
194   we try to bind to it, and if that fails then we don't have that IP
195   on an interface
196  */
197 bool ctdb_sys_have_ip(ctdb_sock_addr *addr)
198 {
199         int s;
200         int ret;
201         
202         addr->ip.sin_port = 0;
203         s = socket(addr->sa.sa_family, SOCK_STREAM, IPPROTO_TCP);
204         if (s == -1) {
205                 return false;
206         }
207         ret = bind(s, (struct sockaddr *)addr, sizeof(ctdb_sock_addr));
208         close(s);
209         return ret == 0;
210 }
211
212
213
214
215 /* This function is used to open a raw socket to capture from
216  */
217 int ctdb_sys_open_capture_socket(const char *iface, void **private_data)
218 {
219         pcap_t *pt;
220
221         pt=pcap_open_live(iface, 100, 0, 0, NULL);
222         if (pt == NULL) {
223                 DEBUG(DEBUG_CRIT,("Failed to open capture device %s\n", iface));
224                 return -1;
225         }
226         *((pcap_t **)private_data) = pt;
227
228         return pcap_fileno(pt);
229 }
230
231
232 /* This function is used to close the capture socket
233  */
234 int ctdb_sys_close_capture_socket(void *private_data)
235 {
236         pcap_t *pt = (pcap_t *)private_data;
237         pcap_close(pt);
238         return 0;
239 }
240
241
242
243 /*
244   send gratuitous arp reply after we have taken over an ip address
245
246   saddr is the address we are trying to claim
247   iface is the interface name we will be using to claim the address
248  */
249 int ctdb_sys_send_arp(const ctdb_sock_addr *addr, const char *iface)
250 {
251         /* We dont do grat arp on aix yet */
252         return 0;
253 }
254
255
256
257 /*
258   get ethernet MAC address on AIX
259  */
260 static int aix_get_mac_addr(const char *device_name, uint8_t mac[6])
261 {
262         size_t ksize;
263         struct kinfo_ndd *ndd;
264         int count, i;
265
266         ksize = getkerninfo(KINFO_NDD, 0, 0, 0);
267         if (ksize == 0) {
268                 errno = ENOSYS;
269                 return -1;
270         }
271
272         ndd = (struct kinfo_ndd *)malloc(ksize);
273         if (ndd == NULL) {
274                 errno = ENOMEM;
275                 return -1;
276         }
277
278         if (getkerninfo(KINFO_NDD, ndd, &ksize, 0) == -1) {
279                 errno = ENOSYS;
280                 return -1;
281         }
282
283         count= ksize/sizeof(struct kinfo_ndd);
284         for (i=0;i<count;i++) {
285                 if ( (ndd[i].ndd_type != NDD_ETHER) 
286                 &&   (ndd[i].ndd_type != NDD_ISO88023) ) {
287                         continue;
288                 }
289                 if (ndd[i].ndd_addrlen != 6) {
290                         continue;
291                 }
292                 if (!(ndd[i].ndd_flags&NDD_UP)) {
293                         continue;
294                 }
295                 if ( strcmp(device_name, ndd[i].ndd_name)
296                 &&   strcmp(device_name, ndd[i].ndd_alias) ) {
297                         continue;
298                 }
299                 memcpy(mac, ndd[i].ndd_addr, 6);
300                 free(ndd);
301                 return 0;
302         }
303         free(ndd);
304         errno = ENOENT;
305         return -1;
306 }
307
308 int ctdb_sys_read_tcp_packet(int s, void *private_data, 
309                         ctdb_sock_addr *src, ctdb_sock_addr *dst,
310                         uint32_t *ack_seq, uint32_t *seq)
311 {
312         int ret;
313         struct ether_header *eth;
314         struct ip *ip;
315         struct ip6_hdr *ip6;
316         struct tcphdr *tcp;
317         struct ctdb_killtcp_connection *conn;
318         struct pcap_pkthdr pkthdr;
319         const u_char *buffer;
320         pcap_t *pt = (pcap_t *)private_data;
321
322         buffer=pcap_next(pt, &pkthdr);
323         if (buffer==NULL) {
324                 return -1;
325         }
326
327         /* Ethernet */
328         eth = (struct ether_header *)buffer;
329
330         /* we want either IPv4 or IPv6 */
331         if (eth->ether_type == htons(ETHERTYPE_IP)) {
332                 /* IP */
333                 ip = (struct ip *)(eth+1);
334
335                 /* We only want IPv4 packets */
336                 if (ip->ip_v != 4) {
337                         return -1;
338                 }
339                 /* Dont look at fragments */
340                 if ((ntohs(ip->ip_off)&0x1fff) != 0) {
341                         return -1;
342                 }
343                 /* we only want TCP */
344                 if (ip->ip_p != IPPROTO_TCP) {
345                         return -1;
346                 }
347
348                 /* make sure its not a short packet */
349                 if (offsetof(struct tcphdr, th_ack) + 4 + 
350                     (ip->ip_hl*4) > ret) {
351                         return -1;
352                 }
353                 /* TCP */
354                 tcp = (struct tcphdr *)((ip->ip_hl*4) + (char *)ip);
355         
356                 /* tell the caller which one we've found */
357                 src->ip.sin_family      = AF_INET;
358                 src->ip.sin_addr.s_addr = ip->ip_src.s_addr;
359                 src->ip.sin_port        = tcp->th_sport;
360                 dst->ip.sin_family      = AF_INET;
361                 dst->ip.sin_addr.s_addr = ip->ip_dst.s_addr;
362                 dst->ip.sin_port        = tcp->th_dport;
363                 *ack_seq                = tcp->th_ack;
364                 *seq                    = tcp->th_seq;
365
366
367                 return 0;
368 #ifndef ETHERTYPE_IP6
369 #define ETHERTYPE_IP6 0x86dd
370 #endif
371         } else if (eth->ether_type == htons(ETHERTYPE_IP6)) {
372                         /* IP6 */
373                 ip6 = (struct ip6_hdr *)(eth+1);
374
375                 /* we only want TCP */
376                 if (ip6->ip6_nxt != IPPROTO_TCP) {
377                         return -1;
378                 }
379
380                 /* TCP */
381                 tcp = (struct tcphdr *)(ip6+1);
382
383                 /* tell the caller which one we've found */
384                 src->ip6.sin6_family = AF_INET6;
385                 src->ip6.sin6_port   = tcp->th_sport;
386                 src->ip6.sin6_addr   = ip6->ip6_src;
387
388                 dst->ip6.sin6_family = AF_INET6;
389                 dst->ip6.sin6_port   = tcp->th_dport;
390                 dst->ip6.sin6_addr   = ip6->ip6_dst;
391
392                 *ack_seq             = tcp->th_ack;
393                 *seq                 = tcp->th_seq;
394
395                 return 0;
396         }
397
398         return -1;
399 }
400
401
402