AppleTalk (EtherTalk) proxy
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

more progress, use pledge("stdio"), better protocol parsing

+300 -152
+2 -2
Makefile
··· 1 - CFLAGS = -O2 -Wall -Wunused -Wmissing-prototypes -Wstrict-prototypes -Wunused 1 + CFLAGS = -O2 -Wall -Wextra -Wunused -Wmissing-prototypes -Wstrict-prototypes 2 2 CFLAGS += -g 3 - CC = cc 3 + CC ?= cc 4 4 5 5 LIBS = -lpcap 6 6
+298 -150
atalk-proxy.c
··· 1 + /* 2 + * Copyright (c) 2026 joshua stein <jcs@jcs.org> 3 + * 4 + * Permission to use, copy, modify, and distribute this software for any 5 + * purpose with or without fee is hereby granted, provided that the above 6 + * copyright notice and this permission notice appear in all copies. 7 + * 8 + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 + */ 16 + 17 + #include <err.h> 18 + #include <ifaddrs.h> 19 + #include <pcap.h> 20 + #include <poll.h> 1 21 #include <stdio.h> 2 22 #include <stdlib.h> 3 - #include <stdarg.h> 4 - #include <err.h> 5 - #include <limits.h> 6 23 #include <string.h> 7 - #include <poll.h> 8 - #include <pcap.h> 9 24 #include <time.h> 10 25 #include <unistd.h> 11 - #include <arpa/inet.h> 26 + #include <net/bpf.h> 27 + #include <net/if_dl.h> 12 28 #include <sys/ioctl.h> 13 29 #include <sys/socket.h> 14 - #include <sys/select.h> 15 - #include <netinet/in.h> 16 - 17 30 #include <sys/types.h> 18 - #include <ifaddrs.h> 19 - #include <net/if_dl.h> 20 31 21 32 #define MIN(a,b) (((a)<(b))?(a):(b)) 22 33 23 34 enum { 35 + IFACE_EGRESS, 24 36 IFACE_TAP, 25 - IFACE_WIFI, 26 37 }; 27 38 28 - struct pcap_pkthdr header; 29 - const u_char *packet; 30 - char errbuf[PCAP_ERRBUF_SIZE]; 31 - char *ifaces[2] = { NULL }; 32 - unsigned char macs[2][6]; 33 - pcap_t *pcaps[2]; 39 + static char errbuf[PCAP_ERRBUF_SIZE]; 40 + static char *ifaces[2] = { NULL }; 41 + static unsigned char emac[6]; 42 + static pcap_t *pcaps[2]; 43 + static int debug = 0; 34 44 45 + uint16_t be16(const unsigned char *); 46 + uint32_t be24(const unsigned char *); 35 47 pcap_t * sniff(char *, char *); 36 48 void usage(void); 37 - void debug(char *, ...); 38 - void packet_handler(u_char *, const struct pcap_pkthdr *, const u_char *); 49 + char * ts(void); 50 + void forward(u_char *, const struct pcap_pkthdr *, const u_char *); 51 + void inspect(unsigned char *, size_t); 39 52 int mac(const char *, unsigned char *); 40 53 41 54 void 42 55 usage(void) 43 56 { 44 - printf("usage: atalk-proxy -t <tap iface> -w <wifi iface>\n"); 57 + printf("usage: atalk-proxy [-d] -e <egress iface> -t <tap iface>\n"); 45 58 exit(1); 46 59 } 47 60 48 - void 49 - debug(char *fmt, ...) 50 - { 51 - va_list ap; 52 - struct timespec ts; 53 - struct tm *tm; 54 - char ti[25]; 55 - long msec; 56 - 57 - clock_gettime(CLOCK_REALTIME, &ts); 58 - if (ts.tv_nsec >= 999500000) { 59 - ts.tv_sec++; 60 - msec = 0; 61 - } else 62 - msec = (ts.tv_nsec + 500000) / 1000000; 63 - 64 - tm = localtime(&ts.tv_sec); 65 - strftime(ti, sizeof(ti), "%H:%M:%S", tm); 66 - printf("[%s.%03li] ", ti, msec); 67 - 68 - va_start(ap, fmt); 69 - vprintf(fmt, ap); 70 - va_end(ap); 71 - } 72 - 73 61 int 74 62 main(int argc, char *argv[]) 75 63 { 76 64 struct pollfd pfd[3]; 77 65 int ch, n, nready, cmplt; 78 66 79 - while ((ch = getopt(argc, argv, "t:w:")) != -1) { 67 + while ((ch = getopt(argc, argv, "de:t:")) != -1) { 80 68 switch (ch) { 81 - case 't': 82 - if ((ifaces[IFACE_TAP] = strdup(optarg)) == NULL) 69 + case 'd': 70 + debug++; 71 + break; 72 + case 'e': 73 + if ((ifaces[IFACE_EGRESS] = strdup(optarg)) == NULL) 83 74 err(1, "strdup"); 84 75 break; 85 - case 'w': 86 - if ((ifaces[IFACE_WIFI] = strdup(optarg)) == NULL) 76 + case 't': 77 + if ((ifaces[IFACE_TAP] = strdup(optarg)) == NULL) 87 78 err(1, "strdup"); 88 79 break; 89 80 default: ··· 91 82 } 92 83 } 93 84 94 - if (ifaces[IFACE_TAP] == NULL || ifaces[IFACE_WIFI] == NULL) 85 + if (ifaces[IFACE_TAP] == NULL || ifaces[IFACE_EGRESS] == NULL) 95 86 usage(); 96 87 97 88 for (n = 0; n < 2; n++) { 89 + /* look for LLC SNAP packets, or bare AARP */ 98 90 pcaps[n] = sniff(ifaces[n], 99 - "(ether[14] == 0xaa && ether[15] == 0xaa) or " /* SNAP */ 100 - "(ether[12] == 0x80 && ether[13] == 0xf3)" /* AARP */ 91 + "(ether[14] == 0xaa && ether[15] == 0xaa) or " 92 + "(ether[12] == 0x80 && ether[13] == 0xf3)" 101 93 ); 102 94 103 95 pfd[n].fd = pcap_get_selectable_fd(pcaps[n]); ··· 108 100 if (ioctl(pfd[n].fd, BIOCSHDRCMPLT, &cmplt) == -1) 109 101 err(1, "failed setting BIOCSHDRCMPLT on %s", 110 102 ifaces[n]); 111 - if (mac(ifaces[n], (unsigned char *)&macs[n]) == -1) 112 - errx(1, "failed getting MAC of %s", ifaces[n]); 103 + 104 + if (n == IFACE_EGRESS) { 105 + if (mac(ifaces[n], (unsigned char *)&emac) == -1) 106 + errx(1, "failed getting MAC of %s", ifaces[n]); 107 + 108 + if (debug) 109 + printf("[%s] [%s] listening on egress using " 110 + "outbound MAC " 111 + "%02x:%02x:%02x:%02x:%02x:%02x\n", 112 + ts(), ifaces[n], emac[0], emac[1], emac[2], 113 + emac[3], emac[4], emac[5]); 114 + } else if (debug) { 115 + printf("[%s] [%s] listening on tap\n", ts(), ifaces[n]); 116 + } 113 117 } 114 118 119 + if (pledge("stdio", NULL) == -1) 120 + err(1, "pledge"); 121 + 115 122 for (;;) { 116 123 nready = poll(pfd, 2, 60 * 1000); 117 124 if (nready == -1) ··· 119 126 120 127 for (n = 0; n < 2; n++) { 121 128 if (pfd[n].revents & (POLLERR|POLLNVAL)) { 122 - debug("pfd[%d] has errors\n", n); 129 + warnx("[%s] poll on %s has errors\n", ts(), 130 + ifaces[n]); 123 131 break; 124 132 } 125 133 if ((pfd[n].revents & (POLLIN|POLLHUP))) 126 - pcap_dispatch(pcaps[n], -1, packet_handler, 134 + pcap_dispatch(pcaps[n], -1, forward, 127 135 (u_char *)&n); 128 136 } 129 137 } ··· 168 176 return handle; 169 177 } 170 178 179 + int 180 + mac(const char *ifname, unsigned char *mac) 181 + { 182 + struct ifaddrs *ifap, *ifa; 183 + struct sockaddr_dl *sdl; 184 + int found = 0; 185 + 186 + if (getifaddrs(&ifap) != 0) 187 + return -1; 188 + 189 + for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { 190 + if (ifa->ifa_addr == NULL || 191 + ifa->ifa_addr->sa_family != AF_LINK || 192 + strcmp(ifa->ifa_name, ifname) != 0) 193 + continue; 194 + 195 + sdl = (struct sockaddr_dl *)ifa->ifa_addr; 196 + if (sdl->sdl_alen == 6) { 197 + memcpy(mac, LLADDR(sdl), 6); 198 + found = 1; 199 + break; 200 + } 201 + } 202 + 203 + freeifaddrs(ifap); 204 + return found ? 0 : -1; 205 + } 206 + 207 + uint16_t 208 + be16(const unsigned char *b) 209 + { 210 + return ((unsigned)b[0] << 8) | b[1]; 211 + } 212 + 213 + uint32_t 214 + be24(const unsigned char *b) 215 + { 216 + return ((unsigned)b[0] << 16) | ((unsigned)b[1] << 8) | b[2]; 217 + } 218 + 219 + char * 220 + ts(void) 221 + { 222 + static char ret[25], ti[20]; 223 + struct timespec ts; 224 + struct tm *tm; 225 + long msec; 226 + 227 + clock_gettime(CLOCK_REALTIME, &ts); 228 + if (ts.tv_nsec >= 999500000) { 229 + ts.tv_sec++; 230 + msec = 0; 231 + } else 232 + msec = (ts.tv_nsec + 500000) / 1000000; 233 + 234 + tm = localtime(&ts.tv_sec); 235 + strftime(ti, sizeof(ti), "%H:%M:%S", tm); 236 + snprintf(ret, sizeof(ret), "%s.%03li", ti, msec); 237 + 238 + return ret; 239 + } 240 + 171 241 void 172 - packet_handler(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes) 242 + forward(u_char *user, const struct pcap_pkthdr *h, const u_char *b) 173 243 { 174 - static unsigned char mbytes[1500]; 244 + static unsigned char mb[1500]; 245 + size_t len; 175 246 int iface = (int)*user; 176 - int n, j, len, ret, oface; 247 + int ret, oface; 177 248 178 249 len = h->len; 179 - if (len > sizeof(mbytes)) 180 - len = sizeof(mbytes); 181 - memcpy(mbytes, bytes, len); 250 + if (len > sizeof(mb)) 251 + len = sizeof(mb); 252 + memcpy(mb, b, len); 182 253 183 - oface = (iface == IFACE_TAP ? IFACE_WIFI : IFACE_TAP); 254 + oface = (iface == IFACE_TAP ? IFACE_EGRESS : IFACE_TAP); 184 255 185 - debug("[%s %zu]", ifaces[iface], h->len); 256 + if (debug) 257 + printf("[%s] [%s] [%3d]", ts(), ifaces[iface], h->len); 186 258 187 - if (bytes[12] == 0x80 && bytes[13] == 0xf3) { 188 - /* bare AARP packet with no LLC, insert LLC */ 189 - mbytes[14] = 0xaa; /* DSAP: SNAP */ 190 - mbytes[15] = 0xaa; /* SSAP: SNAP */ 191 - mbytes[16] = 0x03; /* control */ 259 + if (be16(b + 12) == 0x80f3) { 260 + /* bare AARP packet with no LLC header, insert it */ 261 + if (debug > 1) 262 + printf(" bare AARP, inserting LLC:"); 192 263 193 - mbytes[17] = 0x00; 194 - mbytes[18] = 0x00; 195 - mbytes[19] = 0x00; /* org: none */ 264 + mb[14] = 0xaa; /* DSAP: SNAP */ 265 + mb[15] = 0xaa; /* SSAP: SNAP */ 266 + mb[16] = 0x03; /* control */ 196 267 197 - mbytes[20] = 0x80; 198 - mbytes[21] = 0xf3; /* type: aarp */ 268 + mb[17] = 0x00; 269 + mb[18] = 0x00; 270 + mb[19] = 0x00; /* org: none */ 271 + 272 + mb[20] = 0x80; 273 + mb[21] = 0xf3; /* type: aarp */ 199 274 200 275 /* append AARP */ 201 - memcpy(mbytes + 22, bytes + 14, h->len - 14); 276 + memcpy(mb + 22, b + 14, h->len - 14); 202 277 len = h->len + 8; 203 278 204 279 /* fix length */ 205 - mbytes[12] = 0x00; 206 - mbytes[13] = len - 12 - 2; 280 + mb[12] = 0x00; 281 + mb[13] = len - 12 - 2; 207 282 } 208 283 209 - if (bytes[12] == 0x80 && bytes[13] == 0xf3) { 210 - printf(" AARP"); 284 + if (debug) 285 + inspect(mb, len); 211 286 212 - /* opcode: request */ 213 - printf(": who has %d.%d? tell %d.%d", 214 - ((int)bytes[38] << 16) | (int)bytes[39] << 8 | bytes[40], 215 - bytes[41], 216 - ((int)bytes[28] << 16) | (int)bytes[29] << 8 | bytes[30], 217 - bytes[31]); 218 - } else if (bytes[14] == 0xaa) { 219 - /* DSAP: SNAP */ 220 - printf(" SNAP"); 287 + if (iface == IFACE_TAP) { 288 + /* replace source MAC from tap packet with egress's */ 289 + memcpy(mb + 6, emac, 6); 221 290 222 - if (bytes[15] != 0xaa) 223 - goto relay; 291 + /* for AARP replies we're sending, replace MAC with egress */ 292 + if (mb[14] == 0xaa /* DSAP: SNAP */ && 293 + mb[15] == 0xaa /* SSAP: SNAP */ && 294 + be16(mb + 20) == 0x80f3 /* type: AARP */ && 295 + be16(mb + 28) == 0x0002 /* op: reply */) { 296 + if (debug) 297 + printf(": replacing MAC in outbound AARP reply"); 298 + memcpy(mb + 30, emac, 6); 299 + } 300 + } 224 301 225 - /* SSAP: SNAP */ 302 + if (debug) 303 + printf(" [-> %s]\n", ifaces[oface]); 226 304 227 - if (bytes[20] != 0x80 || bytes[21] != 0x9b) 228 - goto relay; 305 + ret = pcap_inject(pcaps[oface], mb, len); 306 + if (ret != (int)len) 307 + warn("pcap_inject to %s: %s", ifaces[oface], 308 + pcap_geterr(pcaps[oface])); 309 + } 229 310 230 - if (bytes[34] != 2) 231 - goto relay; 311 + void 312 + inspect(unsigned char *pkt, size_t len) 313 + { 314 + size_t tlen, j; 315 + unsigned i, n, op, count; 316 + uint16_t pid; 232 317 233 - /* NBP */ 234 - n = 42; 235 - printf(": \""); 236 - while (n < h->len) { 237 - len = bytes[n++]; 238 - if (n + len > h->len) { 239 - printf(" (overflow %d > %d)", n + len, h->len); 240 - goto relay; 241 - } 242 - for (j = 0; j < len; j++, ++n) 243 - printf("%c", bytes[n]); 244 - } 245 - printf("\""); 318 + printf(" "); 246 319 247 - len = h->len; 248 - goto relay; 320 + if (len < 22) { 321 + printf("bogus length %zu", len); 322 + return; 249 323 } 250 324 251 - relay: 252 - if (oface == IFACE_WIFI) { 253 - /* replace source MAC from tap packet with wifi's */ 254 - memcpy(mbytes + 6, macs[IFACE_WIFI], 6); 325 + if (pkt[14] != 0xaa) { 326 + printf("DSAP 0x%x != SNAP", pkt[14]); 327 + return; 328 + } 255 329 256 - /* if this is an AARP reply, pretend we're at the wifi's MAC */ 257 - if (mbytes[14] == 0xaa /* DSAP: SNAP */ && 258 - mbytes[15] == 0xaa /* SSAP: SNAP */ && 259 - mbytes[20] == 0x80 && mbytes[21] == 0xf3 /* type: AARP */ && 260 - mbytes[28] == 0x00 && mbytes[29] == 0x02 /* op: reply */ && 261 - memcmp(mbytes + 30, macs[IFACE_TAP], 6) == 0) { 262 - printf(": AARP reply, replacing MAC"); 263 - memcpy(mbytes + 30, macs[IFACE_WIFI], 6); 330 + if (pkt[15] != 0xaa) { 331 + printf("SSAP 0x%x != SNAP", pkt[15]); 332 + return; 333 + } 334 + 335 + switch (pid = be16(pkt + 20)) { 336 + case 0x80f3: 337 + printf("AARP: "); 338 + 339 + switch (op = be16(pkt + 28)) { 340 + case 1: 341 + /* request */ 342 + printf("who has %d.%d? tell %d.%d", 343 + be24(pkt + 38), pkt[41], be24(pkt + 28), pkt[31]); 344 + break; 345 + case 2: 346 + /* reply */ 347 + printf("%d.%d is at %02x:%02x:%02x:%02x:%02x:%02x", 348 + be24(pkt + 36), pkt[41], 349 + pkt[30], pkt[31], pkt[32], pkt[33], pkt[34], 350 + pkt[35]); 351 + break; 352 + case 3: 353 + /* probe */ 354 + printf("is there a %d.%d?", be24(pkt + 46), pkt[49]); 355 + break; 356 + default: 357 + printf("unknown operation 0x%02x", op); 264 358 } 359 + return; 360 + case 0x809b: 361 + /* AppleTalk DDP, handled below */ 362 + break; 363 + default: 364 + printf("unknown LLC PID 0x%04x", pid); 365 + return; 265 366 } 266 367 267 - printf(" [-> %s %u]\n", ifaces[oface], len); 368 + /* AppleTalk DDP */ 268 369 269 - ret = pcap_inject(pcaps[oface], mbytes, len); 270 - if (ret != len) 271 - warn("pcap_inject to %s: %s", ifaces[oface], 272 - pcap_geterr(pcaps[oface])); 273 - } 370 + switch (pkt[34]) { 371 + case 2: 372 + /* AppleTalk Name Binding Protocl */ 373 + printf("NBP: "); 374 + count = pkt[35] & 0xf; 274 375 275 - int 276 - mac(const char *ifname, unsigned char *mac) 277 - { 278 - struct ifaddrs *ifap, *ifa; 279 - struct sockaddr_dl *sdl; 280 - int found = 0; 376 + switch (pkt[35] >> 4) { 377 + case 2: 378 + printf("lookup: "); 379 + break; 380 + case 3: 381 + printf("reply[%d]: ", count); 382 + break; 383 + default: 384 + printf("unknown operation %d", pkt[35] >> 4); 385 + return; 386 + } 281 387 282 - if (getifaddrs(&ifap) != 0) 283 - return -1; 388 + n = 37; 389 + for (i = 0; i < count; i++) { 390 + if (i > 0) 391 + printf(", "); 284 392 285 - for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { 286 - if (ifa->ifa_addr == NULL || 287 - ifa->ifa_addr->sa_family != AF_LINK || 288 - strcmp(ifa->ifa_name, ifname) != 0) 289 - continue; 393 + /* network, node, port, enumerator */ 394 + n += 2 + 1 + 1 + 1; 290 395 291 - sdl = (struct sockaddr_dl *)ifa->ifa_addr; 292 - if (sdl->sdl_alen == 6) { 293 - memcpy(mac, LLADDR(sdl), 6); 294 - found = 1; 396 + /* object */ 397 + printf("\""); 398 + tlen = pkt[n++]; 399 + if (n + tlen > len) 400 + goto overflow; 401 + for (j = 0; j < tlen; j++, n++) 402 + printf("%c", pkt[n]); 403 + 404 + /* type */ 405 + printf(":"); 406 + tlen = pkt[n++]; 407 + if (n + tlen > len) 408 + goto overflow; 409 + for (j = 0; j < tlen; j++, n++) 410 + printf("%c", pkt[n]); 411 + 412 + /* zone */ 413 + printf("@"); 414 + tlen = pkt[n++]; 415 + if (n + tlen > len) 416 + goto overflow; 417 + for (j = 0; j < tlen; j++, n++) 418 + printf("%c", pkt[n]); 419 + printf("\""); 420 + } 421 + break; 422 + overflow: 423 + printf(" [overflow %zu > %zu]", n + tlen, len); 424 + break; 425 + case 3: 426 + /* transaction protocol */ 427 + printf("transaction protocol"); 428 + /* TODO */ 429 + break; 430 + case 4: 431 + printf("echo from %d.%d to %d.%d", be16(pkt + 28), pkt[31], 432 + be16(pkt + 26), pkt[30]); 433 + break; 434 + case 6: 435 + printf("ZIP: "); 436 + switch (pkt[35]) { 437 + case 5: 438 + printf("GetNetInfo"); 439 + break; 440 + default: 441 + printf(": unknown ZIP function %d", pkt[35]); 295 442 break; 296 443 } 444 + break; 445 + default: 446 + printf("unknown DDP protocol %d", pkt[34]); 447 + break; 297 448 } 298 - 299 - freeifaddrs(ifap); 300 - return found ? 0 : -1; 301 449 }