├── .gitignore ├── Makefile ├── README.rst └── src ├── swatpd.c └── test.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | swatpd 3 | test 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | CFLAGS = -g -O0 --std=gnu99 -D_GNU_SOURCE -Wall -Werror -pedantic 3 | LDFLAGS = 4 | 5 | all: swatpd test 6 | 7 | swatpd: swatpd.o 8 | swatpd.o: src/swatpd.c 9 | 10 | test: test.o 11 | test.o: src/test.c 12 | 13 | clean: 14 | rm -f *.o swatpd test 15 | 16 | install: 17 | install -c -m 755 swatpd /usr/local/bin 18 | 19 | %: %.o ; gcc $(CFLAGS) -o $@ $^ $(LDFLAGS) 20 | %.o: src/%.c ; gcc $(CFLAGS) -o $@ -c $^ 21 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | .. -*-rst-*- 2 | 3 | ======== 4 | swatpd 5 | ======== 6 | 7 | ------------------------------------------------ 8 | Stolen WiFi Aggregate Tunneling Protocol Dæmon 9 | ------------------------------------------------ 10 | 11 | :Author: Justine Tunney 12 | :Version: 0.1 pre-alpha 13 | :Platform: Linux 14 | :Copyright: (c) 2011 Justine Tunney 15 | :License: GNU AGPL 3 or later 16 | :Disclaimer: This program is a proof of concept 17 | 18 | 19 | Description 20 | =========== 21 | 22 | So you've expropriated every wireless network in your area, the only 23 | problem is none of them work reliably... but what if you could use two 24 | (or more) wireless hotspots simultaneously? That way if one link 25 | starts being unreliable or stops working, your internet will still 26 | operate smoothly. 27 | 28 | swatpd raids your internet connections together like they're hard 29 | drives. It works by tunneling your traffic to an external swatp 30 | server, which then forwards it to the internet. Tunnel traffic is 31 | duplicated onto each of your wireless cards on a per-packet basis. 32 | Whichever packet reaches the swatp server first wins, and duplicates 33 | are discarded. It's like having your own multi-homed BGP router 34 | except with proactive, rather than reactive, reliability. 35 | 36 | I created this program because I got frustrated with my internet 37 | cutting out periodically for a few seconds at a time while while 38 | raiding in World of Warcraft when my neighbor decides to download 39 | porn. I could have just started paying for an internet connection, 40 | but fuck Comcast and Verizon. 41 | 42 | What does it mean? 43 | 44 | * Packet loss will decrease significantly. 45 | 46 | * Downloads won't go any faster. 47 | 48 | * Latency will increase because your packets need to bounce off the 49 | swatp server. 50 | 51 | For me this was about an extra 40ms round-trip time (how long it 52 | takes to ping my server) but in practice probably more like 30ms 53 | because the datacenter in which my server hosted has faster routes 54 | to most places than the local ISPs. 55 | 56 | * Your IP address will be different since traffic is being bounced off 57 | the remote tunnel endpoint. 58 | 59 | Another way to accomplish "double internet" is to use iptables 60 | mark-based routing that randomly delegates tcp/udp streams to specific 61 | routing tables configured to use separate internet connections along 62 | with a script that monitors latency on each link to periodically 63 | adjust marking probability. The only problem with this is that I'm 64 | forced to reconnect to WoW if one of the links goes down... or if both 65 | links are sorta crappy, I'm still screwed. 66 | 67 | 68 | Requirements 69 | ============ 70 | 71 | * A Linux machine 72 | 73 | * Two or more WiFi cards, each connected to different hotspots. 74 | 75 | It's best if you configure each router to be on different channels 76 | to minimize interference. Channels 1, 6, 11, and 14 do not 77 | interfere with each other. It also helps to use 802.11a (instead of 78 | b/g/n) because it's less prone to interference. 79 | 80 | * Somewhere to run the remote swatp endpoint. 81 | 82 | A dedicated server or VPS will do the trick. If you have a friend 83 | with a fast, reliable internet connection and a Linux router, that 84 | will work too. Most importantly, you want the server to be 85 | geographically close to where you live as to minimize any additional 86 | latency. 87 | 88 | * You need to forward port 31337 on each wireless router. 89 | 90 | * If both wireless routers use the same subnet (192.168.0.0/24) this 91 | could potentially cause problems. The simplest way to address this 92 | problem is to configure the router to put your computer in the DMZ. 93 | 94 | 95 | Usage Example 96 | ============= 97 | 98 | Here's a setup with two wifi cards. Both wireless routers were 99 | configured to make "compy" the DMZ host (which means the router's DHCP 100 | server assigns compy a public IP.) 101 | 102 | Here's a diagram:: 103 | 104 | 24.1.1.10:31337 ================================== 105 | /\ || 106 | || wlan0 \/ 107 | +---------+ tun0 tun0 +----------+ eth0 108 | | compy |- - - - - - - - - - - -| server | 6.6.6.6:31337 ==> INTERNET 109 | +---------+ 10.4.4.2 10.4.4.1 +----------+ 110 | || wlan1 /\ 111 | \/ || 112 | 24.2.2.20:31337 ================================== 113 | 114 | Run the following commands on server as root:: 115 | 116 | sysctl -w net.ipv4.ip_forward=1 117 | iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE 118 | swatpd reliable 10.4.4.1/24 31337 \ 119 | eth0 24.1.1.10 31337 \ 120 | eth0 24.2.2.20 31337 121 | 122 | Run the following commands on compy as root:: 123 | 124 | swatpd reliable 10.4.4.2/24 31337 \ 125 | wlan0 6.6.6.6 31337 \ 126 | wlan1 6.6.6.6 31337 127 | ip route change default via 10.4.4.1 128 | for f in /proc/sys/net/ipv4/conf/*/rp_filter; do 129 | echo 0 > $f 130 | done 131 | 132 | This is compy's ``/etc/network/interfaces`` file:: 133 | 134 | auto lo 135 | iface lo inet loopback 136 | 137 | auto wlan0 138 | iface wlan0 inet dhcp 139 | wireless-essid dd-wrt 140 | wireless-key restricted DEADBEEF66 141 | 142 | auto wlan1 143 | iface wlan1 inet dhcp 144 | wireless-essid linksys 145 | 146 | 147 | Tips 148 | ==== 149 | 150 | If you want downloads to go fast try using "fast" instead of 151 | "reliable" in the swatpd command. This will round-robin load balance 152 | packets across your connections. This might not be a good idea if 153 | your internet connections are the least bit unreliable. 154 | 155 | You'll probably want to configure dhcpcd to use OpenDNS to avoid both 156 | routers fighting over who gets to be the DNS server:: 157 | 158 | echo 'prepend domain-name-servers 208.67.222.222;' \ 159 | >>/etc/dhcp3/dhclient.conf 160 | 161 | If you're using Ubuntu, the network manager will ruin everything. You 162 | should configure ``/etc/network/interfaces`` similar to what's in the 163 | example section and then run the following commands:: 164 | 165 | /etc/init.d/network-manager stop 166 | /etc/init.d/avahi-daemon stop 167 | /etc/init.d/networking restart 168 | 169 | 170 | ToDo 171 | ==== 172 | 173 | * Shared-secret encryption 174 | 175 | * Eliminate requirement for port forwarding 176 | 177 | * Create a mode for faster internet that stripes packets across 178 | multiple wifi links rather than duplicating them. For instance, 179 | right now we only support RAID1, we also want RAID0 and RAID5. 180 | 181 | * IPv6 support 182 | 183 | 184 | How to Crack WEP 185 | ================ 186 | 187 | Burn the latest Backtrack Linux ISO and buy an Alfa AWUS036H wireless 188 | card. Boot Backtrack with the wireless card and follow these 189 | instructions. 190 | 191 | Let's look for WEP routers on channel 6 to target:: 192 | 193 | airmon-ng start wlan0 6 194 | airodump-ng -c 6 -t WEP -w output_wep wlan0 195 | 196 | Authenticate to target in second terminal window:: 197 | 198 | aireplay-ng -1 6000 -o 1 -q 10 -h -e wlan0 199 | 200 | Inject packets in third terminal window:: 201 | 202 | aireplay-ng -3 -h -e wlan0 203 | 204 | Wait for first terminal window to collect 30,000 packets. Stop 205 | programs in all three terminals and run:: 206 | 207 | aircrack-ng -b output_wep*.cap 208 | -------------------------------------------------------------------------------- /src/swatpd.c: -------------------------------------------------------------------------------- 1 | /* 2 | * swatp - stolen wifi aggregate tunneling protocol 3 | * copyright (c) 2011 j.a. roberts tunney 4 | * licensed under the gnu agpl 3 or later 5 | */ 6 | 7 | /** 8 | * @file swatpd.c 9 | * @brief stolen wifi aggregate tunneling protocol daemon 10 | */ 11 | 12 | #ifdef HAVE_CONFIG_H 13 | #include 14 | #endif 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | 45 | #define max(a, b) (((a) > (b)) ? (a) : (b)) 46 | #define min(a, b) (((a) < (b)) ? (a) : (b)) 47 | 48 | enum swatp_mode { reliable, fast }; 49 | 50 | struct swatp { 51 | uint32_t magic; 52 | uint32_t type; 53 | uint32_t seq; 54 | }; 55 | 56 | struct tunhdr { 57 | uint16_t flags; 58 | uint16_t proto; /* http://en.wikipedia.org/wiki/EtherType */ 59 | }; 60 | 61 | static bool is_running = true; 62 | 63 | static const uint16_t proto_ipv4 = 0x0800; 64 | static const uint16_t proto_ipv6 = 0x86DD; 65 | 66 | static const int history_max = 1024 * 8; 67 | static const int mtu = (1500 - sizeof(struct iphdr) - sizeof(struct udphdr) - 68 | sizeof(struct swatp) - sizeof(struct tunhdr)); 69 | 70 | static inline bool empty(const char *s) 71 | { 72 | return (!s || s[0] == '\0'); 73 | } 74 | 75 | static inline bool strmatch(const char *s1, const char *s2) 76 | { 77 | if (!s1 || !s2) { 78 | return false; 79 | } else { 80 | return (strcmp(s1, s2) == 0); 81 | } 82 | } 83 | 84 | static int tun_alloc(char *dev) 85 | { 86 | assert(dev); 87 | 88 | struct ifreq ifr[1] = {{{{ 0 }}}}; 89 | ifr->ifr_flags = IFF_TUN; 90 | if (*dev) { 91 | strncpy(ifr->ifr_name, dev, IFNAMSIZ); 92 | } 93 | 94 | int fd; 95 | if ((fd = open("/dev/net/tun", O_RDWR)) < 0) { 96 | perror("open(/dev/net/tun) error"); 97 | exit(1); 98 | } 99 | 100 | if (ioctl(fd, TUNSETIFF, (void *)ifr) < 0) { 101 | perror("ioctl(TUNSETIFF) error"); 102 | exit(1); 103 | } 104 | 105 | strcpy(dev, ifr->ifr_name); 106 | 107 | if (ioctl(fd, TUNSETPERSIST, 0) < 0) { 108 | perror("ioctl(TUNSETPERSIST) error"); 109 | exit(1); 110 | } 111 | 112 | return fd; 113 | } 114 | 115 | static void run(const char *fmt, ...) 116 | { 117 | va_list ap; 118 | char buf[1024 * 16]; 119 | va_start(ap, fmt); 120 | vsnprintf(buf, sizeof(buf), fmt, ap); 121 | va_end(ap); 122 | 123 | int rc = system(buf); 124 | if (WIFSIGNALED(rc) != 0) { 125 | fprintf(stderr, "command failed: %s\n", buf); 126 | exit(1); 127 | } 128 | } 129 | 130 | static int sockin(uint16_t port) 131 | { 132 | int fd; 133 | if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 134 | perror("socket(SOCK_DGRAM) failed"); 135 | exit(1); 136 | } 137 | 138 | struct sockaddr_in sa[1] = {{ 0 }}; 139 | sa->sin_family = AF_INET; 140 | sa->sin_port = htons(port); 141 | sa->sin_addr.s_addr = inet_addr("0.0.0.0"); 142 | if (bind(fd, (struct sockaddr *)sa, sizeof(sa)) < 0) { 143 | perror("bind(SOCK_DGRAM) error"); 144 | exit(1); 145 | } 146 | 147 | return fd; 148 | } 149 | 150 | /** 151 | * Creates a udp socket for sending ip traffic to remote endpoint 152 | * 153 | * The socket is "connected" to the remote endpoint so you can use 154 | * send() instead of sendto(). 155 | * 156 | * We use Linux's SO_BINDTODEVICE feature to bind the socket to a 157 | * specific ethernet device. This means our packets will skip the 158 | * routing table entirely and use arp to figure out the next ethernet 159 | * hop. 160 | * 161 | * This is important because once the tunnel is activated we'll change 162 | * the default route to the tunnel, so we don't want to tunnel our 163 | * tunnel traffic inside our tunnel dawg. 164 | * 165 | * @param dev Required name of network device to use (e.g. eth0, wlan0) 166 | * @param ip Required remote IP address or hostname 167 | * @param port Required remote port number 168 | * @return Socket file descriptor or crash 169 | */ 170 | static int sockout(const char *dev, const char *ip, uint16_t port) 171 | { 172 | int fd; 173 | if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 174 | perror("socket(SOCK_DGRAM) failed"); 175 | exit(1); 176 | } 177 | 178 | struct sockaddr_in sa[1] = {{ 0 }}; 179 | sa->sin_family = AF_INET; 180 | sa->sin_port = htons(port); 181 | sa->sin_addr.s_addr = inet_addr(ip); 182 | if (connect(fd, (struct sockaddr *)sa, sizeof(sa)) < 0) { 183 | perror("connect(SOCK_DGRAM) error"); 184 | exit(1); 185 | } 186 | 187 | struct ifreq ifr[1] = {{{{ 0 }}}}; 188 | snprintf(ifr->ifr_name, sizeof(ifr->ifr_name), "%s", dev); 189 | if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) { 190 | perror("ioctl(SIOCGIFINDEX) error"); 191 | } 192 | if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, 193 | (void *)ifr, sizeof(ifr)) < 0) { 194 | perror("setsockopt(SO_BINDTODEVICE) error"); 195 | } 196 | 197 | return fd; 198 | } 199 | 200 | /* static void log_packet(const char *prefix, struct iphdr *iphdr) */ 201 | /* { */ 202 | /* char ip_src[INET_ADDRSTRLEN]; */ 203 | /* char ip_dst[INET_ADDRSTRLEN]; */ 204 | /* inet_ntop(AF_INET, &(iphdr->saddr), ip_src, sizeof(ip_src)); */ 205 | /* inet_ntop(AF_INET, &(iphdr->daddr), ip_dst, sizeof(ip_dst)); */ 206 | /* printf("%s %s -> %s\n", prefix, ip_src, ip_dst); */ 207 | /* } */ 208 | 209 | /** 210 | * Easier way to generate a set of file descriptors for select() 211 | */ 212 | static int make_fd_set(fd_set *fds, int fd, ...) 213 | { 214 | va_list ap; 215 | int maxfd = 0; 216 | int arg = fd; 217 | va_start(ap, fd); 218 | FD_ZERO(fds); 219 | do { 220 | assert(arg >= 0); 221 | FD_SET(arg, fds); 222 | if (arg > maxfd) { 223 | maxfd = arg; 224 | } 225 | } while ((arg = va_arg(ap, int)) != -1); 226 | va_end(ap); 227 | return maxfd; 228 | } 229 | 230 | static void on_close(int signum) 231 | { 232 | is_running = false; 233 | } 234 | 235 | static enum swatp_mode swatp_mode(const char *smode) 236 | { 237 | if (strmatch(smode, "reliable")) { 238 | return reliable; 239 | } else if (strmatch(smode, "fast")) { 240 | return fast; 241 | } else { 242 | fprintf(stderr, "invalid mode: %s\n", smode); 243 | exit(1); 244 | } 245 | } 246 | 247 | void realtime() 248 | { 249 | /* the highest scheduling priority linux will allow */ 250 | struct sched_param tp[1]; 251 | assert(sched_getparam(0, tp) == 0); 252 | tp->sched_priority = 99; 253 | if (sched_setscheduler(0, SCHED_FIFO, tp) < 0) { 254 | perror("sched_setscheduler(SCHED_FIFO, MAX_PRIO) error"); 255 | } 256 | /* prevent page faults by gobbling up ram */ 257 | if (mlockall(MCL_CURRENT) < 0) { 258 | perror("mlockall(MCL_CURRENT) error"); 259 | } 260 | } 261 | 262 | int main(int argc, const char *argv[]) 263 | { 264 | srand(time(NULL)); 265 | assert(argc >= 1 + 3 + 3); 266 | assert((argc - (1 + 3)) % 3 == 0); 267 | 268 | const enum swatp_mode mode = swatp_mode(argv[1]); 269 | const char *tun_cidr = argv[2]; 270 | const uint16_t listen_port = (uint16_t)atoi(argv[3]); 271 | 272 | /* create tunnel device */ 273 | char tundev[128] = { 0 }; 274 | const int tunfd = tun_alloc(tundev); 275 | run("ip link set %s up", tundev); 276 | run("ip link set %s mtu %d", tundev, mtu); 277 | run("ip addr add %s dev %s", tun_cidr, tundev); 278 | const int skin = sockin(listen_port); 279 | 280 | /* create array of transmit sockets */ 281 | int j = 1 + 3; 282 | const int skouts_len = (argc - j) / 3; 283 | int skouts[skouts_len]; 284 | int skouts_robin = 0; /* for fast mode */ 285 | int n = 0; 286 | while (j < argc && n < 16) { 287 | const char *dev = argv[j + 0]; 288 | const char *ip = argv[j + 1]; 289 | const uint16_t port = (uint16_t)atoi(argv[j + 2]); 290 | skouts[n] = sockout(dev, ip, port); 291 | n += 1; 292 | j += 3; 293 | } 294 | 295 | uint32_t seq = rand(); /* current sequence id for egress frames */ 296 | int seenidx = 0; 297 | int64_t seen[history_max]; /* history of ingress seqs to drop duplicates */ 298 | for (n = 0; n < history_max; n++) { 299 | seen[n] = -1; 300 | } 301 | 302 | uint8_t memory[1024 * 64]; 303 | 304 | /* memory alias for packet including swat header */ 305 | uint8_t *pkt = memory; 306 | struct swatp *hdr = (struct swatp *)pkt; 307 | const int maxamt = sizeof(memory); 308 | const int minamt = (sizeof(struct swatp) + sizeof(struct tunhdr) + 309 | sizeof(struct iphdr)); 310 | 311 | /* memory alias for tun/tap frame */ 312 | uint8_t *tunpkt = pkt + sizeof(struct swatp); 313 | struct tunhdr *tunhdr = (struct tunhdr *)tunpkt; 314 | const int tunmaxamt = maxamt - sizeof(struct swatp); 315 | const int tunminamt = sizeof(struct tunhdr) + sizeof(struct iphdr); 316 | 317 | /* memory alias for ip packet */ 318 | /* uint8_t *ippkt = tunpkt + sizeof(struct tunhdr); */ 319 | /* struct iphdr *iphdr = (struct iphdr *)ippkt; */ 320 | /* const int ipmaxamt = tunmaxamt - sizeof(struct tunhdr); */ 321 | /* const int ipminamt = sizeof(struct iphdr); */ 322 | 323 | realtime(); 324 | signal(SIGINT, on_close); 325 | while (is_running) { 326 | fd_set rfds[1]; 327 | int maxfd = make_fd_set(rfds, tunfd, skin, -1); 328 | int rc = select(maxfd + 1, rfds, NULL, NULL, NULL); 329 | if (rc < 0) { 330 | if (errno == EINTR) { 331 | continue; /* a signal rudely interrupted us */ 332 | } 333 | perror("select() error"); 334 | exit(1); 335 | } 336 | 337 | if (FD_ISSET(tunfd, rfds)) { 338 | /* data from our network, forward to remote endpoint */ 339 | const ssize_t tunamt = read(tunfd, tunpkt, tunmaxamt); 340 | /* const ssize_t ipamt = tunamt - sizeof(struct tunhdr); */ 341 | if (tunamt <= 0) { 342 | perror("read(tunfd) error"); 343 | exit(1); 344 | } 345 | if (tunamt > tunminamt && ntohs(tunhdr->proto) == proto_ipv4) { 346 | /* log_packet(" egress", iphdr); */ 347 | hdr->magic = htonl(0xFeedABee); 348 | hdr->type = htonl(0); 349 | hdr->seq = htonl(++seq); 350 | const int amt = sizeof(struct swatp) + tunamt; 351 | switch (mode) { 352 | case reliable: 353 | for (n = 0; n < skouts_len; n++) { 354 | if (write(skouts[n], pkt, amt) != amt) { 355 | perror("write() error"); 356 | } 357 | } 358 | break; 359 | case fast: 360 | if (write(skouts[skouts_robin], pkt, amt) != amt) { 361 | perror("write() error"); 362 | } 363 | skouts_robin += 1; 364 | skouts_robin = skouts_robin % skouts_len; 365 | break; 366 | } 367 | } 368 | } 369 | 370 | if (FD_ISSET(skin, rfds)) { 371 | /* data from remote endpoint, forward to our network */ 372 | const ssize_t amt = read(skin, pkt, maxamt); 373 | const ssize_t tunamt = amt - sizeof(struct swatp); 374 | /* const ssize_t ipamt = tunamt - sizeof(struct tunhdr); */ 375 | if (amt <= 0) { 376 | perror("read(skin) error"); 377 | exit(1); 378 | } 379 | if (amt > minamt) { 380 | bool drop = false; 381 | const int64_t rseq = (int64_t)ntohl(hdr->seq); 382 | for (n = 0; n < history_max; n++) { 383 | if (rseq == seen[n]) { 384 | /* fprintf(stderr, "dropping dup seq %ld\n", rseq); */ 385 | drop = true; 386 | break; 387 | } 388 | } 389 | if (!drop) { 390 | seen[seenidx] = rseq; 391 | if (++seenidx == history_max) { 392 | seenidx = 0; 393 | } 394 | /* log_packet("ingress", iphdr); */ 395 | write(tunfd, tunpkt, tunamt); 396 | } 397 | } 398 | } 399 | } 400 | 401 | fprintf(stderr, "shutting down\n"); 402 | exit(0); 403 | } 404 | 405 | /* For Emacs: 406 | * Local Variables: 407 | * indent-tabs-mode:nil 408 | * c-basic-offset:4 409 | * c-file-style: nil 410 | * End: 411 | * For VIM: 412 | * vim:set softtabstop=8 shiftwidth=8 tabstop=8: 413 | */ 414 | -------------------------------------------------------------------------------- /src/test.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Send UDP Packet:: 3 | * 4 | * echo hello | socat -4 -u - udp4:localhost:11443 5 | * 6 | * Listen for UDP on specific interface:: 7 | * 8 | * sudo socat - udp4-listen:11443,so-bindtodevice=wlan1 9 | * 10 | * 11 | * 12 | * 13 | */ 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | /* struct sockaddr_in raddr[1]; */ 43 | /* socklen_t addrlen = sizeof(raddr); */ 44 | /* const ssize_t amt = recvfrom( */ 45 | /* skin, pkt, maxamt, 0, (struct sockaddr *)raddr, &addrlen); */ 46 | /* const ssize_t tunamt = amt - sizeof(struct swatp); */ 47 | 48 | static inline bool empty(const char *s) 49 | { 50 | return (!s || s[0] == '\0'); 51 | } 52 | 53 | static inline bool strmatch(const char *s1, const char *s2) 54 | { 55 | if (!s1 || !s2) { 56 | return false; 57 | } else { 58 | return (strcmp(s1, s2) == 0); 59 | } 60 | } 61 | 62 | /** 63 | * Returns IPv4 address associated with device name 64 | * 65 | * For example: get_device_ip4_addr("eth0") => "10.66.6.1" 66 | * 67 | * @param name Name of network device. For example "eth0" 68 | * @return IP of device or NULL. You need to free() this. 69 | */ 70 | static int get_device_ip4_addr(const char *devname, char *ip, size_t ipamt) 71 | { 72 | if (empty(devname) || ip == NULL || ipamt < INET_ADDRSTRLEN) { 73 | return -1; 74 | } 75 | 76 | int fd = -1; 77 | int res = -1; 78 | struct ifreq *ifr = NULL; 79 | struct ifconf ifc[1] = {{ 0 }}; 80 | 81 | if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 82 | perror("socket(SOCK_DGRAM)"); 83 | goto finish; 84 | } 85 | 86 | /* find number of network interfaces */ 87 | ifc->ifc_ifcu.ifcu_req = NULL; 88 | ifc->ifc_len = 0; 89 | if (ioctl(fd, SIOCGIFCONF, ifc) < 0) { 90 | perror("ioctl(SIOCGIFCONF) #1"); 91 | goto finish; 92 | } 93 | const int ifcnt = ifc->ifc_len / sizeof(struct ifreq); 94 | 95 | if ((ifr = malloc(ifc->ifc_len * 2)) == NULL) { 96 | goto finish; 97 | } 98 | 99 | /* request list all device names with their ips */ 100 | ifc->ifc_ifcu.ifcu_req = ifr; 101 | if (ioctl(fd, SIOCGIFCONF, ifc) < 0) { 102 | perror("ioctl(SIOCGIFCONF) #2"); 103 | goto finish; 104 | } 105 | 106 | int n; 107 | for (n = 0; n < ifcnt; n++) { 108 | const struct ifreq *r = &ifr[n]; 109 | const struct sockaddr_in *sin = (struct sockaddr_in *)&(r->ifr_addr); 110 | if (strmatch(r->ifr_name, devname)) { 111 | if (inet_ntop(AF_INET, &(sin->sin_addr), ip, ipamt) == NULL) { 112 | perror("inet_ntop() error"); 113 | } else { 114 | res = 0; 115 | } 116 | break; 117 | } 118 | } 119 | 120 | finish: 121 | if (fd != -1) { close(fd); } 122 | if (ifr) { free(ifr); } 123 | return res; 124 | } 125 | 126 | static int sockin(const char *dev, uint16_t port) 127 | { 128 | int fd; 129 | /* if ((fd = socket(AF_INET, SOCK_RAW, IPPROTO_UDP)) < 0) { */ 130 | if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 131 | /* if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { */ 132 | perror("socket(SOCK_DGRAM) failed"); 133 | exit(1); 134 | } 135 | 136 | char ip[INET_ADDRSTRLEN]; 137 | if (get_device_ip4_addr(dev, ip, sizeof(ip)) < 0) { 138 | fprintf(stderr, "device '%s' doesn't have an ip4 address\n", dev); 139 | exit(1); 140 | } 141 | 142 | snprintf(ip, sizeof(ip), "0.0.0.0"); 143 | 144 | printf("bind %s %s %d\n", dev, ip, port); 145 | 146 | { 147 | struct sockaddr_in sa[1] = {{ 0 }}; 148 | sa->sin_family = AF_INET; 149 | sa->sin_port = htons(port); 150 | if (inet_pton(sa->sin_family, ip, &(sa->sin_addr)) < 0) { 151 | perror("bad connect address"); 152 | exit(1); 153 | } 154 | if (bind(fd, (struct sockaddr *)sa, sizeof(sa)) < 0) { 155 | perror("bind(SOCK_DGRAM) error"); 156 | exit(1); 157 | } 158 | } 159 | 160 | /* struct ifreq ifr[1] = {{{{ 0 }}}}; */ 161 | /* snprintf(ifr->ifr_name, sizeof(ifr->ifr_name), "%s", dev); */ 162 | /* if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) { */ 163 | /* perror("ioctl(SIOCGIFINDEX) error"); */ 164 | /* } else { */ 165 | /* if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, */ 166 | /* (void *)ifr, sizeof(ifr)) < 0) { */ 167 | /* perror("setsockopt(SO_BINDTODEVICE) error"); */ 168 | /* } */ 169 | /* } */ 170 | 171 | /* { */ 172 | /* struct sockaddr_in sa[1] = {{ 0 }}; */ 173 | /* sa->sin_family = AF_INET; */ 174 | /* sa->sin_port = htons(port); */ 175 | /* if (inet_pton(sa->sin_family, "66.55.144.147", &(sa->sin_addr)) < 0) { */ 176 | /* perror("bad connect address"); */ 177 | /* exit(1); */ 178 | /* } */ 179 | /* if (connect(fd, (struct sockaddr *)sa, sizeof(sa)) < 0) { */ 180 | /* perror("bind(SOCK_DGRAM) error"); */ 181 | /* exit(1); */ 182 | /* } */ 183 | /* } */ 184 | 185 | printf("connected\n"); 186 | 187 | return fd; 188 | } 189 | 190 | static char *ipstr4(uint32_t addr) 191 | { 192 | const socklen_t sz = INET_ADDRSTRLEN; 193 | char *res = malloc(sz); 194 | if (res) { 195 | if (inet_ntop(AF_INET, &addr, res, sz) == NULL) { 196 | perror("inet_ntop() error"); 197 | free(res); 198 | res = NULL; 199 | } 200 | } 201 | return res; 202 | } 203 | 204 | static void udpget(int fd) 205 | { 206 | const size_t sz = 1024 * 64; 207 | char buf[sz]; 208 | struct sockaddr_in from[1]; 209 | socklen_t fromsz = sizeof(from); 210 | const int amt = recvfrom(fd, buf, sz, 0, from, &fromsz); 211 | const int hlen = 0; /* sizeof(struct iphdr) + sizeof(struct udphdr); */ 212 | fflush(stdout); 213 | printf("%s:%d sent: %.*s\n", 214 | ipstr4(from->sin_addr.s_addr), ntohs(from->sin_port), 215 | amt - hlen, buf + hlen); 216 | } 217 | 218 | int main(int argc, const char *argv[]) 219 | { 220 | /* const char *const devs[] = { "eth0", "wlan0", "wlan1" }; */ 221 | /* int n; */ 222 | /* for (n = 0; n < 3; n++) { */ 223 | /* const char *const dev = devs[n]; */ 224 | /* char ip[INET_ADDRSTRLEN]; */ 225 | /* assert(get_device_ip4_addr(dev, ip, INET_ADDRSTRLEN) == 0); */ 226 | /* printf("%s = %s\n", dev, ip); */ 227 | /* } */ 228 | 229 | { 230 | const int fd = sockin("wlan0", 11443); 231 | for (;;) { udpget(fd); } 232 | close(fd); 233 | } 234 | 235 | { 236 | const int fd = sockin("wlan1", 11443); 237 | /* for (;;) { udpget(fd); } */ 238 | udpget(fd); 239 | close(fd); 240 | } 241 | 242 | /* { */ 243 | /* const int fd = sockin("wlan0", 11443); */ 244 | /* /\* write(fd, "hello", 5); *\/ */ 245 | /* char buf[1024 * 64]; */ 246 | /* for (n = 0; n < 2; n++) { */ 247 | /* const int amt = read(fd, buf, sizeof(buf)); */ 248 | /* const int hlen = 0; //sizeof(struct iphdr) + sizeof(struct udphdr); */ 249 | /* printf("got: %.*s\n", amt - hlen, buf + hlen); */ 250 | /* } */ 251 | /* close(fd); */ 252 | /* } */ 253 | 254 | /* printf("\n"); */ 255 | 256 | /* { */ 257 | /* const int fd = sockin("wlan1", 11443); */ 258 | /* /\* write(fd, "hello", 5); *\/ */ 259 | /* char buf[1024 * 64]; */ 260 | /* const int amt = read(fd, buf, sizeof(buf)); */ 261 | /* const int hlen = 0; //sizeof(struct iphdr) + sizeof(struct udphdr); */ 262 | /* printf("got: %.*s\n", amt - hlen, buf + hlen); */ 263 | /* close(fd); */ 264 | /* } */ 265 | 266 | return 0; 267 | } 268 | 269 | /* For Emacs: 270 | * Local Variables: 271 | * indent-tabs-mode:nil 272 | * c-basic-offset:4 273 | * c-file-style: nil 274 | * End: 275 | * For VIM: 276 | * vim:set softtabstop=8 shiftwidth=8 tabstop=8: 277 | */ 278 | --------------------------------------------------------------------------------