forked from markmcconnell/mai
-
Notifications
You must be signed in to change notification settings - Fork 0
/
sock.c
164 lines (120 loc) · 5.02 KB
/
sock.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
#include "mai.h"
/* ######################################################################## */
const char *if_name; // multicast interface name
const uint8_t if_local[10]; // multicast interface l2 address
unsigned int if_mtu; // multicast interface mtu
int if_index; // multicast interface index
struct in_addr if_addr; // multicast interface address
/* ######################################################################## */
static inline int setsockopt_i(int sk, int level, int name, int value) {
int opt = value;
return(setsockopt(sk, level, name, &opt, sizeof(opt)));
}
/* ######################################################################## */
static int sock_send(const char *ip, const uint16_t port) {
// convert target address
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
if (!inet_aton(ip, &addr.sin_addr))
return(mai_error("target address (%s): %m\n", ip));
// create udp4 socket
int sk;
if ((sk = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
return(mai_error("socket: %m\n"));
// set outbound multicast interface
if (if_name && setsockopt(sk, IPPROTO_IP, IP_MULTICAST_IF, &if_addr, sizeof(if_addr)))
return(mai_error("multicast interface: %m\n"));
// set type of service (equivalent to DSCP AF41)
if (setsockopt_i(sk, IPPROTO_IP, IP_TOS, IPTOS_PREC_FLASHOVERRIDE|IPTOS_THROUGHPUT))
return(mai_error("ip tos: %m\n"));
// set time to live
if (setsockopt_i(sk, IPPROTO_IP, IP_MULTICAST_TTL, 32))
return(mai_error("multicast ttl: %m\n"));
// set default send address
if (connect(sk, (struct sockaddr *)&addr, sizeof(addr)))
return(mai_error("connect: %m\n"));
return(sk);
}
/* ######################################################################## */
static int sock_recv(const char *ip, const uint16_t port) {
// setup multicast group request
struct ip_mreqn req = (struct ip_mreqn){ .imr_ifindex = if_index, .imr_address.s_addr = htonl(INADDR_ANY) };
if (!inet_aton(ip, &req.imr_multiaddr))
return(mai_error("multicast address (%s): %m.\n", ip));
// create udp4 socket
int sk;
if ((sk = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
return(mai_error("create socket: %m.\n"));
// join multicast group to start receving packets
if (setsockopt(sk, IPPROTO_IP, IP_ADD_MEMBERSHIP, &req, sizeof(req)))
return(mai_error("ip add membership: %m\n"));
// let other processes bind to same port
if (setsockopt_i(sk, SOL_SOCKET, SO_REUSEPORT, 1))
return(mai_error("reuse port: %m\n"));
// let other processes bind to same address
if (setsockopt_i(sk, SOL_SOCKET, SO_REUSEADDR, 1))
return(mai_error("reuse address: %m\n"));
// bind to port and multicast destination
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr = req.imr_multiaddr;
if (bind(sk, (struct sockaddr *)&addr, sizeof(addr)))
return(mai_error("bind: %m\n"));
// finished, success
return(sk);
}
/* ######################################################################## */
int mai_sock_open(int mode, const char *ip, const uint16_t port) {
return((mode == 's') ? sock_send(ip, port) : sock_recv(ip, port));
}
/* ######################################################################## */
int mai_sock_if_set(const char *name) {
if (!name || !name[0])
return(0);
if ((if_name = strdup(name)) == NULL)
return(mai_error("strdup: %m\n"));
int sk;
if ((sk = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
return(mai_error("socket: %m\n"));
// setup interface request
struct ifreq ifr;
strncpy(ifr.ifr_name, if_name, IFNAMSIZ);
ifr.ifr_name[IFNAMSIZ-1] = 0;
// ask for interface mtu
if (ioctl(sk, SIOCGIFMTU, &ifr))
return(mai_error("get interface mtu (%s): %m\n", if_name));
if_mtu = ifr.ifr_mtu;
// ask for interface index
if (ioctl(sk, SIOCGIFINDEX, &ifr))
return(mai_error("get interface index (%s): %m\n", if_name));
if_index = ifr.ifr_ifindex;
// ask for primary ipv4 address
ifr.ifr_addr.sa_family = AF_INET;
if (ioctl(sk, SIOCGIFADDR, &ifr))
return(mai_error("get interface address (%s): %m\n", if_name));
if_addr = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr;
// ask for layer2 address
if (ioctl(sk, SIOCGIFHWADDR, &ifr))
return(mai_error("get interface hardware address (%s): %m\n", if_name));
uint8_t *out = (uint8_t *)if_local;
*out++ = ifr.ifr_hwaddr.sa_data[0];
*out++ = ifr.ifr_hwaddr.sa_data[1];
*out++ = ifr.ifr_hwaddr.sa_data[2];
*out++ = 0xFF;
*out++ = 0xFE;
*out++ = ifr.ifr_hwaddr.sa_data[3];
*out++ = ifr.ifr_hwaddr.sa_data[4];
*out++ = ifr.ifr_hwaddr.sa_data[5];
*out++ = 0x00;
*out++ = 0x02;
close(sk);
return(0);
}
/* ######################################################################## */
size_t mai_sock_if_mtu(void) { return( if_mtu); }
const void *mai_sock_if_addr(void) { return(&if_addr); }
const char *mai_sock_if_name(void) { return( if_name); }
void mai_sock_if_local(uint8_t *out) { memcpy(out, if_local, sizeof(if_local)); }
/* ######################################################################## */