=== modified file 'src/network.c' --- upstream/src/network.c 2010-06-18 16:34:34 +0000 +++ ipv6_tftp/src/network.c 2010-06-18 17:41:40 +0000 @@ -400,7 +400,8 @@ !fix_fd(tcpfd) || bind(tcpfd, (struct sockaddr *)&addr, sa_len(&addr)) == -1 || listen(tcpfd, 5) == -1 || - bind(fd, (struct sockaddr *)&addr, sa_len(&addr)) == -1) + bind(fd, (struct sockaddr *)&addr, sa_len(&addr)) == -1) +// TODO: we leak fd & tcpfd here return 0; /* The API changed around Linux 2.6.14 but the old ABI is still supported: @@ -437,6 +438,80 @@ return 1; } + +/* c&p ... bad */ +static int create_ipv6_listener_tftp(struct listener **link, int port) +{ + union mysockaddr addr; + int fd; + struct listener *l; + int opt = 1; + + memset(&addr, 0, sizeof(addr)); + addr.in6.sin6_family = AF_INET6; + addr.in6.sin6_addr = in6addr_any; + addr.in6.sin6_port = htons(port); +#ifdef HAVE_SOCKADDR_SA_LEN + addr.in6.sin6_len = sizeof(addr.in6); +#endif + + /* No error of the kernel doesn't support IPv6 */ + if ((fd = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) + return (errno == EPROTONOSUPPORT || + errno == EAFNOSUPPORT || + errno == EINVAL); + + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 || + setsockopt(fd, IPV6_LEVEL, IPV6_V6ONLY, &opt, sizeof(opt)) == -1 || + !fix_fd(fd) || + bind(fd, (struct sockaddr *)&addr, sa_len(&addr)) == -1) + { + close(fd); + return 0; + } + + /* The API changed around Linux 2.6.14 but the old ABI is still supported: + handle all combinations of headers and kernel. + OpenWrt note that this fixes the problem addressed by your very broken patch. */ + + daemon->v6pktinfo = IPV6_PKTINFO; + +#ifdef IPV6_RECVPKTINFO +# ifdef IPV6_2292PKTINFO + if (setsockopt(fd, IPV6_LEVEL, IPV6_RECVPKTINFO, &opt, sizeof(opt)) == -1) + { + if (errno == ENOPROTOOPT && setsockopt(fd, IPV6_LEVEL, IPV6_2292PKTINFO, &opt, sizeof(opt)) != -1) + daemon->v6pktinfo = IPV6_2292PKTINFO; + else + return 0; + } +# else + if (setsockopt(fd, IPV6_LEVEL, IPV6_RECVPKTINFO, &opt, sizeof(opt)) == -1) + return 0; +# endif +#else + if (setsockopt(fd, IPV6_LEVEL, IPV6_PKTINFO, &opt, sizeof(opt)) == -1) + return 0; +#endif + + if(*link) + { + l = *link; + l->tftpfd = fd; + } + else + { + l = safe_malloc(sizeof(struct listener)); + l->fd = -1; + l->tcpfd = -1; + l->tftpfd = fd; + l->family = AF_INET6; + l->next = NULL; + *link = l; + } + + return 1; +} #endif struct listener *create_wildcard_listeners(void) @@ -456,11 +531,11 @@ if (daemon->port != 0) { - + if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1 || (tcpfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) return NULL; - + if (setsockopt(tcpfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 || bind(tcpfd, (struct sockaddr *)&addr, sa_len(&addr)) == -1 || listen(tcpfd, 5) == -1 || @@ -475,30 +550,33 @@ #elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF) setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt)) == -1 || setsockopt(fd, IPPROTO_IP, IP_RECVIF, &opt, sizeof(opt)) == -1 || -#endif +#endif bind(fd, (struct sockaddr *)&addr, sa_len(&addr)) == -1) return NULL; } - + #ifdef HAVE_TFTP if (daemon->tftp_unlimited || daemon->tftp_interfaces) { addr.in.sin_port = htons(TFTP_PORT); if ((tftpfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) return NULL; - + if (!fix_fd(tftpfd) || +#ifdef HAVE_IPV6 + !create_ipv6_listener_tftp(&l6, TFTP_PORT) || +#endif #if defined(HAVE_LINUX_NETWORK) setsockopt(tftpfd, SOL_IP, IP_PKTINFO, &opt, sizeof(opt)) == -1 || #elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF) setsockopt(tftpfd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt)) == -1 || setsockopt(tftpfd, IPPROTO_IP, IP_RECVIF, &opt, sizeof(opt)) == -1 || -#endif +#endif bind(tftpfd, (struct sockaddr *)&addr, sa_len(&addr)) == -1) return NULL; } #endif - + l = safe_malloc(sizeof(struct listener)); l->family = AF_INET; l->fd = fd; @@ -590,6 +668,19 @@ die(_("failed to create TFTP socket: %s"), NULL, EC_BADNET); iface->addr.in.sin_port = save; } +#ifdef HAVE_IPV6 + else if (iface->addr.sa.sa_family == AF_INET6 && iface->tftp_ok) + { + short save = iface->addr.in6.sin6_port; + iface->addr.in6.sin6_port = htons(TFTP_PORT); + if ((new->tftpfd = socket(AF_INET6, SOCK_DGRAM, 0)) == -1 || + setsockopt(new->tftpfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1 || + !fix_fd(new->tftpfd) || + bind(new->tftpfd, &iface->addr.sa, sa_len(&iface->addr)) == -1) + die(_("failed to create TFTP socket: %s"), NULL, EC_BADNET); + iface->addr.in6.sin6_port = save; + } +#endif #endif }