union sockaddru { struct sockaddr sa; struct sockaddr_in sin; struct sockaddr_in6 sin6; }; unsigned parse_scope(char *str) { int sockfd = socket(AF_INET, SOCK_STREAM, 0); struct ifreq ifr; memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, str, sizeof(ifr.ifr_name)); if (0 == ioctl(sockfd, SIOCGIFINDEX, &ifr)) { close(sockfd); return ifr.ifr_ifindex; } printf("ioctl(sockfd, SIOCGIFINDEX, '%s'): %s\n", str, strerror(errno)); exit(2); } union sockaddru *parse_sockaddru(char *_str) { char *str = strdup(_str); union sockaddru *sau = calloc(1, sizeof(*sau)); char *percent = strchr(str, '%'); char *at = strchr(str, '@'); int port = 1701; if (at) { port = atoi(at + 1); if ((port < 0) || (port > 65535)) { printf("Invalid port number: %d\n", port); exit(1); } *at = 0; } if (strchr(str, ':')) { sau->sin6.sin6_family = AF_INET6; sau->sin6.sin6_port = htons(port); if (percent) *percent = 0; if (!inet_pton(AF_INET6, str, &sau->sin6.sin6_addr)) { printf("Could not parse '%s' as IPv6\n", str); exit(1); } if (percent) sau->sin6.sin6_scope_id = parse_scope(percent + 1); printf("port = %u\n", (unsigned)(ntohs(sau->sin6.sin6_port))); } else { if (percent) { printf("scope not supported in IPv4 address\n"); exit(1); } sau->sin.sin_family = AF_INET; sau->sin.sin_port = htons(port); printf("port = %u\n", (unsigned)(ntohs(sau->sin.sin_port))); if (!inet_pton(AF_INET, str, &sau->sin.sin_addr)) { printf("Could not parse '%s' as IPv4\n", str); exit(1); } } return sau; } char *format_su(union sockaddru *su, char *tmp, size_t tmp_len) { const char *scope = ""; const char *a; char t2[64], t3[16]; unsigned short port; if (su->sa.sa_family == AF_INET) { a = inet_ntop(AF_INET, &su->sin.sin_addr, t2, sizeof t2); port = ntohs(su->sin.sin_port); } else if (su->sa.sa_family == AF_INET6) { a = inet_ntop(AF_INET6, &su->sin6.sin6_addr, t2, sizeof t2); port = ntohs(su->sin6.sin6_port); if (su->sin6.sin6_scope_id) { snprintf(t3, sizeof(t3), "%%%u", su->sin6.sin6_scope_id); scope = t3; } } else return NULL; snprintf(tmp, tmp_len, "%s%s %d", a, scope, port); return tmp; } #if 0 void print_p_sa_u(union pppol2tp_sa_union *p_sa_u, int len) { char tmp[64]; if (len == sizeof(p_sa_u->sa4)) printf( "sa4 {\n" " sa_family = %u\n" " sa_protocol = %u\n" " .pid = %d\n" " .fd = %d\n" " .addr = %s\n" " .s_tunnel = %u\n" " .s_session = %u\n" " .d_tunnel = %u\n" " .d_session = %u\n}\n", (unsigned)p_sa_u->sa4.sa_family, (unsigned)p_sa_u->sa4.sa_protocol, (int)p_sa_u->sa4.pppol2tp.pid, (int)p_sa_u->sa4.pppol2tp.fd, format_su((void *)&p_sa_u->sa4.pppol2tp.addr, tmp, sizeof(tmp)), (unsigned)p_sa_u->sa4.pppol2tp.s_tunnel, (unsigned)p_sa_u->sa4.pppol2tp.s_session, (unsigned)p_sa_u->sa4.pppol2tp.d_tunnel, (unsigned)p_sa_u->sa4.pppol2tp.d_session); else if (len == sizeof(p_sa_u->sa6)) printf( "sa6 {\n" " sa_family = %u\n" " sa_protocol = %u\n" " .pid = %d\n" " .fd = %d\n" " .addr = %s\n" " .s_tunnel = %u\n" " .s_session = %u\n" " .d_tunnel = %u\n" " .d_session = %u\n}\n", (unsigned)p_sa_u->sa6.sa_family, (unsigned)p_sa_u->sa6.sa_protocol, (int)p_sa_u->sa6.pppol2tp.pid, (int)p_sa_u->sa6.pppol2tp.fd, format_su((void *)&p_sa_u->sa6.pppol2tp.addr, tmp, sizeof(tmp)), (unsigned)p_sa_u->sa6.pppol2tp.s_tunnel, (unsigned)p_sa_u->sa6.pppol2tp.s_session, (unsigned)p_sa_u->sa6.pppol2tp.d_tunnel, (unsigned)p_sa_u->sa6.pppol2tp.d_session); else if (len == sizeof(p_sa_u->sa4_v3)) ; else if (len == sizeof(p_sa_u->sa6_v3)) ; else { fprintf(stderr, "print_p_sa_u: unknown len(%d)!\n", len); exit(2); } } #endif