ctdb-protocol: Add utility function ctdb_sock_addr_to_string
[Samba.git] / ctdb / common / system_gnu.c
blob12462050e866dde7c798ac424a50e1cf032f4650
1 /*
2 ctdb system specific code to manage raw sockets on linux
4 Copyright (C) Ronnie Sahlberg 2007
5 Copyright (C) Andrew Tridgell 2007
6 Copyright (C) Marc Dequènes (Duck) 2009
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, see <http://www.gnu.org/licenses/>.
22 This file is a copy of 'common/system_linux.c' adapted for Hurd needs,
23 and inspired by 'common/system_aix.c' for the pcap usage.
26 #include "replace.h"
27 #include "system/network.h"
28 #include "system/filesys.h"
29 #include "system/wait.h"
31 #include "lib/util/debug.h"
33 #include "protocol/protocol.h"
35 #include <net/ethernet.h>
36 #include <netinet/ip6.h>
37 #include <net/if_arp.h>
38 #include <pcap.h>
40 #include "common/logging.h"
41 #include "common/system.h"
43 #ifndef ETHERTYPE_IP6
44 #define ETHERTYPE_IP6 0x86dd
45 #endif
48 calculate the tcp checksum for tcp over ipv6
50 static uint16_t tcp_checksum6(uint16_t *data, size_t n, struct ip6_hdr *ip6)
52 uint32_t phdr[2];
53 uint32_t sum = 0;
54 uint16_t sum2;
56 sum += uint16_checksum((uint16_t *)(void *)&ip6->ip6_src, 16);
57 sum += uint16_checksum((uint16_t *)(void *)&ip6->ip6_dst, 16);
59 phdr[0] = htonl(n);
60 phdr[1] = htonl(ip6->ip6_nxt);
61 sum += uint16_checksum((uint16_t *)phdr, 8);
63 sum += uint16_checksum(data, n);
65 sum = (sum & 0xFFFF) + (sum >> 16);
66 sum = (sum & 0xFFFF) + (sum >> 16);
67 sum2 = htons(sum);
68 sum2 = ~sum2;
69 if (sum2 == 0) {
70 return 0xFFFF;
72 return sum2;
76 send gratuitous arp reply after we have taken over an ip address
78 saddr is the address we are trying to claim
79 iface is the interface name we will be using to claim the address
81 int ctdb_sys_send_arp(const ctdb_sock_addr *addr, const char *iface)
83 /* FIXME GNU/Hurd: We don't do gratuitous arp yet */
84 return -1;
89 simple TCP checksum - assumes data is multiple of 2 bytes long
91 static uint16_t tcp_checksum(uint16_t *data, size_t n, struct iphdr *ip)
93 uint32_t sum = uint16_checksum(data, n);
94 uint16_t sum2;
95 sum += uint16_checksum((uint16_t *)(void *)&ip->saddr,
96 sizeof(ip->saddr));
97 sum += uint16_checksum((uint16_t *)(void *)&ip->daddr,
98 sizeof(ip->daddr));
99 sum += ip->protocol + n;
100 sum = (sum & 0xFFFF) + (sum >> 16);
101 sum = (sum & 0xFFFF) + (sum >> 16);
102 sum2 = htons(sum);
103 sum2 = ~sum2;
104 if (sum2 == 0) {
105 return 0xFFFF;
107 return sum2;
111 Send tcp segment from the specified IP/port to the specified
112 destination IP/port.
114 This is used to trigger the receiving host into sending its own ACK,
115 which should trigger early detection of TCP reset by the client
116 after IP takeover
118 This can also be used to send RST segments (if rst is true) and also
119 if correct seq and ack numbers are provided.
121 int ctdb_sys_send_tcp(const ctdb_sock_addr *dest,
122 const ctdb_sock_addr *src,
123 uint32_t seq, uint32_t ack, int rst)
125 int s;
126 int ret;
127 uint32_t one = 1;
128 uint16_t tmpport;
129 ctdb_sock_addr *tmpdest;
130 struct {
131 struct iphdr ip;
132 struct tcphdr tcp;
133 } ip4pkt;
134 struct {
135 struct ip6_hdr ip6;
136 struct tcphdr tcp;
137 } ip6pkt;
139 switch (src->ip.sin_family) {
140 case AF_INET:
141 ZERO_STRUCT(ip4pkt);
142 ip4pkt.ip.version = 4;
143 ip4pkt.ip.ihl = sizeof(ip4pkt.ip)/4;
144 ip4pkt.ip.tot_len = htons(sizeof(ip4pkt));
145 ip4pkt.ip.ttl = 255;
146 ip4pkt.ip.protocol = IPPROTO_TCP;
147 ip4pkt.ip.saddr = src->ip.sin_addr.s_addr;
148 ip4pkt.ip.daddr = dest->ip.sin_addr.s_addr;
149 ip4pkt.ip.check = 0;
151 ip4pkt.tcp.source = src->ip.sin_port;
152 ip4pkt.tcp.dest = dest->ip.sin_port;
153 ip4pkt.tcp.seq = seq;
154 ip4pkt.tcp.ack_seq = ack;
155 ip4pkt.tcp.ack = 1;
156 if (rst) {
157 ip4pkt.tcp.rst = 1;
159 ip4pkt.tcp.doff = sizeof(ip4pkt.tcp)/4;
160 /* this makes it easier to spot in a sniffer */
161 ip4pkt.tcp.window = htons(1234);
162 ip4pkt.tcp.check = tcp_checksum((uint16_t *)&ip4pkt.tcp, sizeof(ip4pkt.tcp), &ip4pkt.ip);
164 /* open a raw socket to send this segment from */
165 s = socket(AF_INET, SOCK_RAW, htons(IPPROTO_RAW));
166 if (s == -1) {
167 DEBUG(DEBUG_CRIT,(__location__ " failed to open raw socket (%s)\n",
168 strerror(errno)));
169 return -1;
172 ret = setsockopt(s, IPPROTO_IP, IP_HDRINCL, &one, sizeof(one));
173 if (ret != 0) {
174 DEBUG(DEBUG_CRIT,(__location__ " failed to setup IP headers (%s)\n",
175 strerror(errno)));
176 close(s);
177 return -1;
180 set_nonblocking(s);
181 set_close_on_exec(s);
183 ret = sendto(s, &ip4pkt, sizeof(ip4pkt), 0, &dest->ip, sizeof(dest->ip));
184 close(s);
185 if (ret != sizeof(ip4pkt)) {
186 DEBUG(DEBUG_CRIT,(__location__ " failed sendto (%s)\n", strerror(errno)));
187 return -1;
189 break;
190 case AF_INET6:
191 ZERO_STRUCT(ip6pkt);
192 ip6pkt.ip6.ip6_vfc = 0x60;
193 ip6pkt.ip6.ip6_plen = htons(20);
194 ip6pkt.ip6.ip6_nxt = IPPROTO_TCP;
195 ip6pkt.ip6.ip6_hlim = 64;
196 ip6pkt.ip6.ip6_src = src->ip6.sin6_addr;
197 ip6pkt.ip6.ip6_dst = dest->ip6.sin6_addr;
199 ip6pkt.tcp.source = src->ip6.sin6_port;
200 ip6pkt.tcp.dest = dest->ip6.sin6_port;
201 ip6pkt.tcp.seq = seq;
202 ip6pkt.tcp.ack_seq = ack;
203 ip6pkt.tcp.ack = 1;
204 if (rst) {
205 ip6pkt.tcp.rst = 1;
207 ip6pkt.tcp.doff = sizeof(ip6pkt.tcp)/4;
208 /* this makes it easier to spot in a sniffer */
209 ip6pkt.tcp.window = htons(1234);
210 ip6pkt.tcp.check = tcp_checksum6((uint16_t *)&ip6pkt.tcp, sizeof(ip6pkt.tcp), &ip6pkt.ip6);
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;
218 /* sendto() don't like if the port is set and the socket is
219 in raw mode.
221 tmpdest = discard_const(dest);
222 tmpport = tmpdest->ip6.sin6_port;
224 tmpdest->ip6.sin6_port = 0;
225 ret = sendto(s, &ip6pkt, sizeof(ip6pkt), 0, &dest->ip6, sizeof(dest->ip6));
226 tmpdest->ip6.sin6_port = tmpport;
227 close(s);
229 if (ret != sizeof(ip6pkt)) {
230 DEBUG(DEBUG_CRIT,(__location__ " failed sendto (%s)\n", strerror(errno)));
231 return -1;
233 break;
235 default:
236 DEBUG(DEBUG_CRIT,(__location__ " not an ipv4/v6 address\n"));
237 return -1;
240 return 0;
244 This function is used to open a raw socket to capture from
246 int ctdb_sys_open_capture_socket(const char *iface, void **private_data)
248 pcap_t *pt;
250 pt=pcap_open_live(iface, 100, 0, 0, NULL);
251 if (pt == NULL) {
252 DEBUG(DEBUG_CRIT,("Failed to open capture device %s\n", iface));
253 return -1;
255 *((pcap_t **)private_data) = pt;
257 return pcap_fileno(pt);
260 /* This function is used to close the capture socket
262 int ctdb_sys_close_capture_socket(void *private_data)
264 pcap_t *pt = (pcap_t *)private_data;
265 pcap_close(pt);
266 return 0;
271 called when the raw socket becomes readable
273 int ctdb_sys_read_tcp_packet(int s, void *private_data,
274 ctdb_sock_addr *src, ctdb_sock_addr *dst,
275 uint32_t *ack_seq, uint32_t *seq)
277 int ret;
278 #define RCVPKTSIZE 100
279 char pkt[RCVPKTSIZE];
280 struct ether_header *eth;
281 struct iphdr *ip;
282 struct ip6_hdr *ip6;
283 struct tcphdr *tcp;
285 ret = recv(s, pkt, RCVPKTSIZE, MSG_TRUNC);
286 if (ret < sizeof(*eth)+sizeof(*ip)) {
287 return -1;
290 /* Ethernet */
291 eth = (struct ether_header *)pkt;
293 /* we want either IPv4 or IPv6 */
294 if (ntohs(eth->ether_type) == ETHERTYPE_IP) {
295 /* IP */
296 ip = (struct iphdr *)(eth+1);
298 /* We only want IPv4 packets */
299 if (ip->version != 4) {
300 return -1;
302 /* Dont look at fragments */
303 if ((ntohs(ip->frag_off)&0x1fff) != 0) {
304 return -1;
306 /* we only want TCP */
307 if (ip->protocol != IPPROTO_TCP) {
308 return -1;
311 /* make sure its not a short packet */
312 if (offsetof(struct tcphdr, ack_seq) + 4 +
313 (ip->ihl*4) + sizeof(*eth) > ret) {
314 return -1;
316 /* TCP */
317 tcp = (struct tcphdr *)((ip->ihl*4) + (char *)ip);
319 /* tell the caller which one we've found */
320 src->ip.sin_family = AF_INET;
321 src->ip.sin_addr.s_addr = ip->saddr;
322 src->ip.sin_port = tcp->source;
323 dst->ip.sin_family = AF_INET;
324 dst->ip.sin_addr.s_addr = ip->daddr;
325 dst->ip.sin_port = tcp->dest;
326 *ack_seq = tcp->ack_seq;
327 *seq = tcp->seq;
329 return 0;
330 } else if (ntohs(eth->ether_type) == ETHERTYPE_IP6) {
331 /* IP6 */
332 ip6 = (struct ip6_hdr *)(eth+1);
334 /* we only want TCP */
335 if (ip6->ip6_nxt != IPPROTO_TCP) {
336 return -1;
339 /* TCP */
340 tcp = (struct tcphdr *)(ip6+1);
342 /* tell the caller which one we've found */
343 src->ip6.sin6_family = AF_INET6;
344 src->ip6.sin6_port = tcp->source;
345 src->ip6.sin6_addr = ip6->ip6_src;
347 dst->ip6.sin6_family = AF_INET6;
348 dst->ip6.sin6_port = tcp->dest;
349 dst->ip6.sin6_addr = ip6->ip6_dst;
351 *ack_seq = tcp->ack_seq;
352 *seq = tcp->seq;
354 return 0;
357 return -1;
360 bool ctdb_sys_check_iface_exists(const char *iface)
362 /* FIXME GNU/Hurd: Interface always considered present */
363 return true;
366 int ctdb_get_peer_pid(const int fd, pid_t *peer_pid)
368 /* FIXME GNU/Hurd: get_peer_pid not implemented */
369 return 1;