ctdb-daemon: Separate prototypes for system specific functions
[samba.git] / ctdb / common / system_freebsd.c
1 /* 
2    ctdb system specific code to manage raw sockets on freebsd
3
4    Copyright (C) Ronnie Sahlberg  2007
5    Copyright (C) Andrew Tridgell  2007
6    Copyright (C) Marc Dequènes (Duck) 2009
7    Copyright (C) Volker Lendecke 2012
8
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, see <http://www.gnu.org/licenses/>.
21
22
23   This file is a copy of 'common/system_linux.c' adapted for Hurd^W kFreeBSD
24   needs, and inspired by 'common/system_aix.c' for the pcap usage.
25 */
26
27 #include "includes.h"
28 #include "system/network.h"
29 #include "system/filesys.h"
30 #include "system/wait.h"
31 #include "../include/ctdb_private.h"
32 #include <net/ethernet.h>
33 #include <netinet/ip6.h>
34 #include <net/if_arp.h>
35 #include <pcap.h>
36
37 #include "common/system.h"
38
39 #ifndef ETHERTYPE_IP6
40 #define ETHERTYPE_IP6 0x86dd
41 #endif
42
43 /*
44   calculate the tcp checksum for tcp over ipv6
45 */
46 static uint16_t tcp_checksum6(uint16_t *data, size_t n, struct ip6_hdr *ip6)
47 {
48         uint32_t phdr[2];
49         uint32_t sum = 0;
50         uint16_t sum2;
51
52         sum += uint16_checksum((uint16_t *)(void *)&ip6->ip6_src, 16);
53         sum += uint16_checksum((uint16_t *)(void *)&ip6->ip6_dst, 16);
54
55         phdr[0] = htonl(n);
56         phdr[1] = htonl(ip6->ip6_nxt);
57         sum += uint16_checksum((uint16_t *)phdr, 8);
58
59         sum += uint16_checksum(data, n);
60
61         sum = (sum & 0xFFFF) + (sum >> 16);
62         sum = (sum & 0xFFFF) + (sum >> 16);
63         sum2 = htons(sum);
64         sum2 = ~sum2;
65         if (sum2 == 0) {
66                 return 0xFFFF;
67         }
68         return sum2;
69 }
70
71 /*
72   send gratuitous arp reply after we have taken over an ip address
73
74   saddr is the address we are trying to claim
75   iface is the interface name we will be using to claim the address
76  */
77 int ctdb_sys_send_arp(const ctdb_sock_addr *addr, const char *iface)
78 {
79         /* FIXME FreeBSD: We dont do gratuitous arp yet */
80         return -1;
81 }
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         sum += uint16_checksum((uint16_t *)(void *)&ip->ip_src,
92                                sizeof(ip->ip_src));
93         sum += uint16_checksum((uint16_t *)(void *)&ip->ip_dst,
94                                sizeof(ip->ip_dst));
95         sum += ip->ip_p + n;
96         sum = (sum & 0xFFFF) + (sum >> 16);
97         sum = (sum & 0xFFFF) + (sum >> 16);
98         sum2 = htons(sum);
99         sum2 = ~sum2;
100         if (sum2 == 0) {
101                 return 0xFFFF;
102         }
103         return sum2;
104 }
105
106 /*
107   Send tcp segment from the specified IP/port to the specified
108   destination IP/port. 
109
110   This is used to trigger the receiving host into sending its own ACK,
111   which should trigger early detection of TCP reset by the client
112   after IP takeover
113
114   This can also be used to send RST segments (if rst is true) and also
115   if correct seq and ack numbers are provided.
116  */
117 int ctdb_sys_send_tcp(const ctdb_sock_addr *dest, 
118                       const ctdb_sock_addr *src,
119                       uint32_t seq, uint32_t ack, int rst)
120 {
121         int s;
122         int ret;
123         uint32_t one = 1;
124         uint16_t tmpport;
125         ctdb_sock_addr *tmpdest;
126         struct {
127                 struct ip ip;
128                 struct tcphdr tcp;
129         } ip4pkt;
130         struct {
131                 struct ip6_hdr ip6;
132                 struct tcphdr tcp;
133         } ip6pkt;
134
135         switch (src->ip.sin_family) {
136         case AF_INET:
137                 ZERO_STRUCT(ip4pkt);
138                 ip4pkt.ip.ip_v  = 4;
139                 ip4pkt.ip.ip_hl    = sizeof(ip4pkt.ip)/4;
140                 ip4pkt.ip.ip_len   = htons(sizeof(ip4pkt));
141                 ip4pkt.ip.ip_ttl   = 255;
142                 ip4pkt.ip.ip_p     = IPPROTO_TCP;
143                 ip4pkt.ip.ip_src.s_addr = src->ip.sin_addr.s_addr;
144                 ip4pkt.ip.ip_dst.s_addr = dest->ip.sin_addr.s_addr;
145                 ip4pkt.ip.ip_sum   = 0;
146
147                 ip4pkt.tcp.th_sport = src->ip.sin_port;
148                 ip4pkt.tcp.th_dport = dest->ip.sin_port;
149                 ip4pkt.tcp.th_seq   = seq;
150                 ip4pkt.tcp.th_ack   = ack;
151                 ip4pkt.tcp.th_flags = 0;
152                 ip4pkt.tcp.th_flags |= TH_ACK;
153                 if (rst) {
154                         ip4pkt.tcp.th_flags |= TH_RST;
155                 }
156                 ip4pkt.tcp.th_off   = sizeof(ip4pkt.tcp)/4;
157                 /* this makes it easier to spot in a sniffer */
158                 ip4pkt.tcp.th_win   = htons(1234);
159                 ip4pkt.tcp.th_sum   = tcp_checksum((uint16_t *)&ip4pkt.tcp, sizeof(ip4pkt.tcp), &ip4pkt.ip);
160
161                 /* open a raw socket to send this segment from */
162                 s = socket(AF_INET, SOCK_RAW, htons(IPPROTO_RAW));
163                 if (s == -1) {
164                         DEBUG(DEBUG_CRIT,(__location__ " failed to open raw socket (%s)\n",
165                                  strerror(errno)));
166                         return -1;
167                 }
168
169                 ret = setsockopt(s, IPPROTO_IP, IP_HDRINCL, &one, sizeof(one));
170                 if (ret != 0) {
171                         DEBUG(DEBUG_CRIT,(__location__ " failed to setup IP headers (%s)\n",
172                                  strerror(errno)));
173                         close(s);
174                         return -1;
175                 }
176
177                 set_nonblocking(s);
178                 set_close_on_exec(s);
179
180                 ret = sendto(s, &ip4pkt, sizeof(ip4pkt), 0,
181                              (const struct sockaddr *)&dest->ip,
182                              sizeof(dest->ip));
183                 close(s);
184                 if (ret != sizeof(ip4pkt)) {
185                         DEBUG(DEBUG_CRIT,(__location__ " failed sendto (%s)\n", strerror(errno)));
186                         return -1;
187                 }
188                 break;
189         case AF_INET6:
190                 ZERO_STRUCT(ip6pkt);
191                 ip6pkt.ip6.ip6_vfc  = 0x60;
192                 ip6pkt.ip6.ip6_plen = htons(20);
193                 ip6pkt.ip6.ip6_nxt  = IPPROTO_TCP;
194                 ip6pkt.ip6.ip6_hlim = 64;
195                 ip6pkt.ip6.ip6_src  = src->ip6.sin6_addr;
196                 ip6pkt.ip6.ip6_dst  = dest->ip6.sin6_addr;
197
198                 ip6pkt.tcp.th_sport = src->ip6.sin6_port;
199                 ip6pkt.tcp.th_dport = dest->ip6.sin6_port;
200                 ip6pkt.tcp.th_seq   = seq;
201                 ip6pkt.tcp.th_ack   = ack;
202                 ip6pkt.tcp.th_flags = 0;
203                 ip6pkt.tcp.th_flags |= TH_ACK;
204                 if (rst) {
205                         ip6pkt.tcp.th_flags |= TH_RST;
206                 }
207                 ip6pkt.tcp.th_off   = sizeof(ip6pkt.tcp)/4;
208                 /* this makes it easier to spot in a sniffer */
209                 ip6pkt.tcp.th_win   = htons(1234);
210                 ip6pkt.tcp.th_sum   = tcp_checksum6((uint16_t *)&ip6pkt.tcp, sizeof(ip6pkt.tcp), &ip6pkt.ip6);
211
212                 s = socket(PF_INET6, SOCK_RAW, IPPROTO_RAW);
213                 if (s == -1) {
214                         DEBUG(DEBUG_CRIT, (__location__ " Failed to open sending socket\n"));
215                         return -1;
216
217                 }
218                 /* sendto() dont like if the port is set and the socket is
219                    in raw mode.
220                 */
221                 tmpdest = discard_const(dest);
222                 tmpport = tmpdest->ip6.sin6_port;
223
224                 tmpdest->ip6.sin6_port = 0;
225                 ret = sendto(s, &ip6pkt, sizeof(ip6pkt), 0,
226                              (const struct sockaddr *)&dest->ip6,
227                              sizeof(dest->ip6));
228                 tmpdest->ip6.sin6_port = tmpport;
229                 close(s);
230
231                 if (ret != sizeof(ip6pkt)) {
232                         DEBUG(DEBUG_CRIT,(__location__ " failed sendto (%s)\n", strerror(errno)));
233                         return -1;
234                 }
235                 break;
236
237         default:
238                 DEBUG(DEBUG_CRIT,(__location__ " not an ipv4/v6 address\n"));
239                 return -1;
240         }
241
242         return 0;
243 }
244
245 /* 
246    This function is used to open a raw socket to capture from
247  */
248 int ctdb_sys_open_capture_socket(const char *iface, void **private_data)
249 {
250         pcap_t *pt;
251
252         pt=pcap_open_live(iface, 100, 0, 0, NULL);
253         if (pt == NULL) {
254                 DEBUG(DEBUG_CRIT,("Failed to open capture device %s\n", iface));
255                 return -1;
256         }
257         *((pcap_t **)private_data) = pt;
258
259         return pcap_fileno(pt);
260 }
261
262 /* This function is used to close the capture socket
263  */
264 int ctdb_sys_close_capture_socket(void *private_data)
265 {
266         pcap_t *pt = (pcap_t *)private_data;
267         pcap_close(pt);
268         return 0;
269 }
270
271
272 /*
273   called when the raw socket becomes readable
274  */
275 int ctdb_sys_read_tcp_packet(int s, void *private_data, 
276                         ctdb_sock_addr *src, ctdb_sock_addr *dst,
277                         uint32_t *ack_seq, uint32_t *seq)
278 {
279         int ret;
280 #define RCVPKTSIZE 100
281         char pkt[RCVPKTSIZE];
282         struct ether_header *eth;
283         struct ip *ip;
284         struct ip6_hdr *ip6;
285         struct tcphdr *tcp;
286
287         ret = recv(s, pkt, RCVPKTSIZE, MSG_TRUNC);
288         if (ret < sizeof(*eth)+sizeof(*ip)) {
289                 return -1;
290         }
291
292         /* Ethernet */
293         eth = (struct ether_header *)pkt;
294
295         /* we want either IPv4 or IPv6 */
296         if (ntohs(eth->ether_type) == ETHERTYPE_IP) {
297                 /* IP */
298                 ip = (struct ip *)(eth+1);
299
300                 /* We only want IPv4 packets */
301                 if (ip->ip_v != 4) {
302                         return -1;
303                 }
304                 /* Dont look at fragments */
305                 if ((ntohs(ip->ip_off)&0x1fff) != 0) {
306                         return -1;
307                 }
308                 /* we only want TCP */
309                 if (ip->ip_p != IPPROTO_TCP) {
310                         return -1;
311                 }
312
313                 /* make sure its not a short packet */
314                 if (offsetof(struct tcphdr, th_ack) + 4 + 
315                     (ip->ip_hl*4) + sizeof(*eth) > ret) {
316                         return -1;
317                 }
318                 /* TCP */
319                 tcp = (struct tcphdr *)((ip->ip_hl*4) + (char *)ip);
320
321                 /* tell the caller which one we've found */
322                 src->ip.sin_family      = AF_INET;
323                 src->ip.sin_addr.s_addr = ip->ip_src.s_addr;
324                 src->ip.sin_port        = tcp->th_sport;
325                 dst->ip.sin_family      = AF_INET;
326                 dst->ip.sin_addr.s_addr = ip->ip_dst.s_addr;
327                 dst->ip.sin_port        = tcp->th_dport;
328                 *ack_seq                = tcp->th_ack;
329                 *seq                    = tcp->th_seq;
330
331                 return 0;
332         } else if (ntohs(eth->ether_type) == ETHERTYPE_IP6) {
333                 /* IP6 */
334                 ip6 = (struct ip6_hdr *)(eth+1);
335
336                 /* we only want TCP */
337                 if (ip6->ip6_nxt != IPPROTO_TCP) {
338                         return -1;
339                 }
340
341                 /* TCP */
342                 tcp = (struct tcphdr *)(ip6+1);
343
344                 /* tell the caller which one we've found */
345                 src->ip6.sin6_family = AF_INET6;
346                 src->ip6.sin6_port   = tcp->th_sport;
347                 src->ip6.sin6_addr   = ip6->ip6_src;
348
349                 dst->ip6.sin6_family = AF_INET6;
350                 dst->ip6.sin6_port   = tcp->th_dport;
351                 dst->ip6.sin6_addr   = ip6->ip6_dst;
352
353                 *ack_seq             = tcp->th_ack;
354                 *seq                 = tcp->th_seq;
355
356                 return 0;
357         }
358
359         return -1;
360 }
361
362 bool ctdb_sys_check_iface_exists(const char *iface)
363 {
364         /* FIXME FreeBSD: Interface always considered present */
365         return true;
366 }
367
368 int ctdb_get_peer_pid(const int fd, pid_t *peer_pid)
369 {
370         /* FIXME FreeBSD: get_peer_pid not implemented */
371         return 1;
372 }
373
374 int ctdb_set_process_name(const char *name)
375 {
376         /* FIXME FreeBSD: set_process_name not implemented */
377         return -ENOSYS;
378 }