Simon Kelley schrieb: > Adding IPv6 support would be fairly simple: the DNS part of dnsmasq does > do IPv6, so all the bits are already in place, there just need to be an > IPv6 socket listening as well as an IPv4 one, and a couple of address > fields in structures need to be extended to hold IPv6 addresses. >
step one: widen the addresses used in tftp.c for use with IPv4 & IPv6 only compile tested, i do not have any netboot stuff. > Cheers, > > Simon. > Greetings Jan -- // Replaces with spaces the braces in cases where // braces in places cause stasis $str = str_replace(array("\{","\}")," ",$str);
=== modified file 'src/dnsmasq.h' --- upstream/src/dnsmasq.h 2010-05-21 10:12:20 +0000 +++ ipv6_tftp/src/dnsmasq.h 2010-06-18 16:23:46 +0000 @@ -601,7 +601,7 @@ int backoff; unsigned int block, blocksize, expansion; off_t offset; - struct sockaddr_in peer; + union mysockaddr peer; char opt_blocksize, opt_transize, netascii, carrylf; struct tftp_file *file; struct tftp_transfer *next; @@ -790,6 +790,7 @@ char *print_mac(char *buff, unsigned char *mac, int len); void bump_maxfd(int fd, int *max); int read_write(int fd, unsigned char *packet, int size, int rw); +const char *mysockaddr_print(const union mysockaddr *src, char *dst, unsigned cnt); /* log.c */ void die(char *message, char *arg1, int exit_code); @@ -824,6 +825,7 @@ struct listener *create_wildcard_listeners(void); struct listener *create_bound_listeners(void); int iface_check(int family, struct all_addr *addr, char *name, int *indexp); +int iface_check_mysockaddr(union mysockaddr *addr, char *name, int *indexp); int fix_fd(int fd); struct in_addr get_ifaddr(char *intr); === modified file 'src/network.c' --- upstream/src/network.c 2010-06-18 14:22:05 +0000 +++ ipv6_tftp/src/network.c 2010-06-18 16:23:49 +0000 @@ -186,6 +186,17 @@ return ret; } + +int iface_check_mysockaddr(union mysockaddr *addr, char *name, int *indexp) +{ + if (AF_INET == addr->sa.sa_family) + return iface_check(addr->sa.sa_family, (struct all_addr *)&addr->in.sin_addr, name, indexp); +#ifdef HAVE_IPV6 + else if (AF_INET6 == addr->sa.sa_family) + return iface_check(addr->sa.sa_family, (struct all_addr *)&addr->in6.sin6_addr, name, indexp); +#endif + return 0; +} static int iface_allowed(struct irec **irecp, int if_index, union mysockaddr *addr, struct in_addr netmask) === modified file 'src/tftp.c' --- upstream/src/tftp.c 2010-05-21 10:10:06 +0000 +++ ipv6_tftp/src/tftp.c 2010-06-18 16:33:32 +0000 @@ -20,7 +20,7 @@ static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, int special); static void free_transfer(struct tftp_transfer *transfer); -static ssize_t tftp_err(int err, char *packet, char *mess, char *file); +static ssize_t tftp_err(int err, char *packet, const char *mess, const char *file); static ssize_t tftp_err_oops(char *packet, char *file); static ssize_t get_block(char *packet, struct tftp_transfer *transfer); static char *next(char **p, char *end); @@ -43,7 +43,7 @@ ssize_t len; char *packet = daemon->packet; char *filename, *mode, *p, *end, *opt; - struct sockaddr_in addr, peer; + union mysockaddr addr, peer; struct msghdr msg; struct iovec iov; struct ifreq ifr; @@ -89,10 +89,16 @@ if ((len = recvmsg(listen->tftpfd, &msg, 0)) < 2) return; - + + memset(&addr, 0, sizeof(addr)); +// TODO: get proper address family + addr.sa.sa_family = AF_INET; +#ifdef HAVE_SOCKADDR_SA_LEN + addr.sa.sa_len = sizeof(addr); +#endif if (daemon->options & OPT_NOWILD) { - addr = listen->iface->addr.in; + addr.in = listen->iface->addr.in; mtu = listen->iface->mtu; name = listen->iface->name; } @@ -102,8 +108,8 @@ int check; struct interface_list *ir; - addr.sin_addr.s_addr = 0; - +// TODO: this looks like recvfromto + /* and needs to be pimped for IPv6 */ #if defined(HAVE_LINUX_NETWORK) for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) if (cmptr->cmsg_level == SOL_IP && cmptr->cmsg_type == IP_PKTINFO) @@ -113,10 +119,10 @@ struct in_pktinfo *p; } p; p.c = CMSG_DATA(cmptr); - addr.sin_addr = p.p->ipi_spec_dst; + addr.in.sin_addr = p.p->ipi_spec_dst; if_index = p.p->ipi_ifindex; } - + #elif defined(HAVE_SOLARIS_NETWORK) for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) { @@ -127,7 +133,7 @@ } p; p.c = CMSG_DATA(cmptr); if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR) - addr.sin_addr = *(p.a); + addr.in.sin_addr = *(p.a); else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF) if_index = *(p.i); } @@ -142,19 +148,25 @@ } p; p.c = CMSG_DATA(cmptr); if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVDSTADDR) - addr.sin_addr = *(p.a); + addr.in.sin_addr = *(p.a); else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_RECVIF) if_index = p.s->sdl_index; } - -#endif - - if (!indextoname(listen->tftpfd, if_index, namebuff) || - addr.sin_addr.s_addr == 0) + +#endif + + if (!indextoname(listen->tftpfd, if_index, namebuff)) + return; + + if ((AF_INET == addr.sa.sa_family && addr.in.sin_addr.s_addr == 0) +#ifdef HAVE_IPV6 + || (AF_INET6 == addr.sa.sa_family && IN6_IS_ADDR_UNSPECIFIED(&addr.in6.sin6_addr)) +#endif + ) return; name = namebuff; - check = iface_check(AF_INET, (struct all_addr *)&addr.sin_addr, name, &if_index); + check = iface_check_mysockaddr(&addr, name, &if_index); /* wierd TFTP service override */ for (ir = daemon->tftp_interfaces; ir; ir = ir->next) @@ -176,9 +188,9 @@ strncpy(ifr.ifr_name, name, IF_NAMESIZE); if (ioctl(listen->tftpfd, SIOCGIFMTU, &ifr) != -1) - mtu = ifr.ifr_mtu; + mtu = ifr.ifr_mtu; } - + /* check for per-interface prefix */ for (pref = daemon->if_prefix; pref; pref = pref->next) if (strcmp(pref->interface, name) == 0) @@ -189,21 +201,22 @@ if (strcmp(ir->interface, name) == 0) special = 1; - addr.sin_port = htons(port); - addr.sin_family = AF_INET; -#ifdef HAVE_SOCKADDR_SA_LEN - addr.sin_len = sizeof(addr); + if (AF_INET == addr.in.sin_family) + addr.in.sin_port = htons(port); +#ifdef HAVE_IPV6 + else + addr.in6.sin6_port = htons(port); #endif - + if (!(transfer = whine_malloc(sizeof(struct tftp_transfer)))) return; - + if ((transfer->sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { free(transfer); return; } - + transfer->peer = peer; transfer->timeout = now + 2; transfer->backoff = 1; @@ -227,7 +240,12 @@ { if (++port <= daemon->end_tftp_port) { - addr.sin_port = htons(port); + if (AF_INET == addr.sa.sa_family) + addr.in.sin_port = htons(port); +#ifdef HAVE_IPV6 + else + addr.in6.sin6_port = htons(port); +#endif continue; } my_syslog(MS_TFTP | LOG_ERR, _("unable to get free port for TFTP")); @@ -237,7 +255,7 @@ } break; } - + p = packet + 2; end = packet + len; @@ -245,7 +263,11 @@ !(filename = next(&p, end)) || !(mode = next(&p, end)) || (strcasecmp(mode, "octet") != 0 && strcasecmp(mode, "netascii") != 0)) - len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), inet_ntoa(peer.sin_addr)); + { + char istr[46]; + len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), + mysockaddr_print(&peer, istr, sizeof(istr))); + } else { if (strcasecmp(mode, "netascii") == 0) @@ -294,15 +316,17 @@ { size_t oldlen = strlen(daemon->namebuff); struct stat statbuf; - - strncat(daemon->namebuff, inet_ntoa(peer.sin_addr), (MAXDNAME-1) - strlen(daemon->namebuff)); + +// TODO: +-1 ??? + mysockaddr_print(&peer, daemon->namebuff + strlen(daemon->namebuff), + (MAXDNAME-1) - strlen(daemon->namebuff)); strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff)); - + /* remove unique-directory if it doesn't exist */ if (stat(daemon->namebuff, &statbuf) == -1 || !S_ISDIR(statbuf.st_mode)) daemon->namebuff[oldlen] = 0; } - + /* Absolute pathnames OK if they match prefix */ if (filename[0] == '/') { @@ -325,20 +349,22 @@ is_err = 0; } } - + while (sendto(transfer->sockfd, packet, len, 0, (struct sockaddr *)&peer, sizeof(peer)) == -1 && errno == EINTR); - + if (is_err) free_transfer(transfer); else { - my_syslog(MS_TFTP | LOG_INFO, _("sent %s to %s"), daemon->namebuff, inet_ntoa(peer.sin_addr)); + char istr[46]; + my_syslog(MS_TFTP | LOG_INFO, _("sent %s to %s"), daemon->namebuff, + mysockaddr_print(&peer, istr, sizeof(istr))); transfer->next = daemon->tftp_trans; daemon->tftp_trans = transfer; } } - + static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, int special) { char *packet = daemon->packet, *namebuff = daemon->namebuff; @@ -351,7 +377,7 @@ /* trick to ban moving out of the subtree */ if (prefix && strstr(namebuff, "/../")) goto perm; - + if ((fd = open(namebuff, O_RDONLY)) == -1) { if (errno == ENOENT) @@ -364,11 +390,11 @@ else goto oops; } - + /* stat the file descriptor to avoid stat->open races */ if (fstat(fd, &statbuf) == -1) goto oops; - + /* running as root, must be world-readable */ if (uid == 0) { @@ -378,7 +404,7 @@ /* in secure mode, must be owned by user running dnsmasq */ else if (!special && (daemon->options & OPT_TFTP_SECURE) && uid != statbuf.st_uid) goto perm; - + /* If we're doing many tranfers from the same file, only open it once this saves lots of file descriptors when mass-booting a big cluster, for instance. @@ -426,24 +452,24 @@ { struct tftp_transfer *transfer, *tmp, **up; ssize_t len; - + struct ack { unsigned short op, block; } *mess = (struct ack *)daemon->packet; - + /* Check for activity on any existing transfers */ for (transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; transfer = tmp) { tmp = transfer->next; - + if (FD_ISSET(transfer->sockfd, rset)) { /* we overwrote the buffer... */ daemon->srv_save = NULL; - + if ((len = recv(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0)) >= (ssize_t)sizeof(struct ack)) { - if (ntohs(mess->op) == OP_ACK && ntohs(mess->block) == (unsigned short)transfer->block) + if (ntohs(mess->op) == OP_ACK && ntohs(mess->block) == (unsigned short)transfer->block) { /* Got ack, ensure we take the (re)transmit path */ transfer->timeout = now; @@ -453,6 +479,7 @@ } else if (ntohs(mess->op) == OP_ERR) { + char istr[46]; char *p = daemon->packet + sizeof(struct ack); char *end = daemon->packet + len; char *err = next(&p, end); @@ -468,26 +495,26 @@ *q = 0; } my_syslog(MS_TFTP | LOG_ERR, _("error %d %s received from %s"), - (int)ntohs(mess->block), err, - inet_ntoa(transfer->peer.sin_addr)); - + (int)ntohs(mess->block), err, + mysockaddr_print(&transfer->peer, istr, sizeof(istr))); + /* Got err, ensure we take abort */ transfer->timeout = now; transfer->backoff = 100; } } } - + if (difftime(now, transfer->timeout) >= 0.0) { int endcon = 0; /* timeout, retransmit */ transfer->timeout += 1 + (1<<transfer->backoff); - + /* we overwrote the buffer... */ daemon->srv_save = NULL; - + if ((len = get_block(daemon->packet, transfer)) == -1) { len = tftp_err_oops(daemon->packet, transfer->file->filename); @@ -498,15 +525,18 @@ /* don't complain about timeout when we're awaiting the last ACK, some clients never send it */ if (len != 0) - my_syslog(MS_TFTP | LOG_ERR, _("failed sending %s to %s"), - transfer->file->filename, inet_ntoa(transfer->peer.sin_addr)); + { + char istr[46]; + my_syslog(MS_TFTP | LOG_ERR, _("failed sending %s to %s"), + transfer->file->filename, mysockaddr_print(&transfer->peer, istr, sizeof(istr))); + } len = 0; } - + if (len != 0) while(sendto(transfer->sockfd, daemon->packet, len, 0, (struct sockaddr *)&transfer->peer, sizeof(transfer->peer)) == -1 && errno == EINTR); - + if (endcon || len == 0) { /* unlink */ @@ -545,7 +575,7 @@ return ret; } -static ssize_t tftp_err(int err, char *packet, char *message, char *file) +static ssize_t tftp_err(int err, char *packet, const char *message, const char *file) { struct errmess { unsigned short op, err; === modified file 'src/util.c' --- upstream/src/util.c 2010-01-23 19:48:37 +0000 +++ ipv6_tftp/src/util.c 2010-06-18 16:19:32 +0000 @@ -512,3 +512,16 @@ return 1; } +const char *mysockaddr_print(const union mysockaddr *src, char *dst, unsigned cnt) +{ +#ifdef HAVE_IPV6 + return inet_ntop(src->sa.sa_family, + AF_INET == src->sa.sa_family ? + (const void *)&src->in.sin_addr : + (const void *)&src->in6.sin6_addr, + dst, cnt); +#else + char *tmp = inet_ntoa(src->in.sin_addr); + return strncpy(dst, tmp, cnt); +#endif +}