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