Re: [Dnsmasq-discuss] TFTP for IPv6

2010-06-18 Thread Jan 'RedBully' Seiffert
Jan 'RedBully' Seiffert schrieb:
> Jan 'RedBully' Seiffert schrieb:
>> 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
>>
>>
> 
> step two:
> tell the udpfromto copy how to deal with ipv6
> 

and the final stepp three:
Add tftp IPv6 listener, a little bit C&P, but...

And then a little cleanup of things i saw for myself.

only compile tested

Greetings
Jan

-- 
Every bug you find is the last one.
=== modified file 'src/network.c'
--- upstream/src/network.c  2010-06-18 16:34:34 +
+++ ipv6_tftp/src/network.c 2010-06-18 17:41:40 +
@@ -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 lis

Re: [Dnsmasq-discuss] TFTP for IPv6

2010-06-18 Thread Jan 'RedBully' Seiffert
Jan 'RedBully' Seiffert schrieb:
> 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
> 
> 

step two:
tell the udpfromto copy how to deal with ipv6

> only compile tested, i do not have any netboot stuff.

same, but i tested code like that in another project of mine, so should work,
minus bugs...

Greetings
Jan

-- 
Every program in development at MIT expands until it can read mail.
=== modified file 'src/tftp.c'
--- upstream/src/tftp.c 2010-06-18 16:34:34 +
+++ ipv6_tftp/src/tftp.c2010-06-18 17:09:35 +
@@ -90,15 +90,9 @@
   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.in = listen->iface->addr.in;
+  addr = listen->iface->addr;
   mtu = listen->iface->mtu;
   name = listen->iface->name;
 }
@@ -108,8 +102,13 @@
   int check;
   struct interface_list *ir;
 
+  memset(&addr, 0, sizeof(addr));
+#ifdef HAVE_SOCKADDR_SA_LEN
+  addr.sa.sa_len = sizeof(addr);
+#endif
+  addr.sa.sa_family = AF_INET;
+
 // 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)
@@ -122,6 +121,19 @@
addr.in.sin_addr = p.p->ipi_spec_dst;
if_index = p.p->ipi_ifindex;
  }
+#ifdef HAVE_IPV6
+   else if (cmptr->cmsg_level == SOL_IPV6 && cmptr->cmsg_type == 
daemon->v6pktinfo)
+ {
+   union {
+ unsigned char *c;
+ struct in6_pktinfo *p;
+   } p;
+   p.c = CMSG_DATA(cmptr);
+   addr.in6.sin6_family = AF_INET6;
+   memcpy(&addr.in6.sin6_addr, &p.p->ipi6_addr, 
sizeof(addr.in6.sin6_addr));
+   if_index = p.p->ipi6_ifindex;
+ }
+#endif
 
 #elif defined(HAVE_SOLARIS_NETWORK)
   for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, 
cmptr))
@@ -136,6 +148,14 @@
addr.in.sin_addr = *(p.a);
  else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == 
IP_RECVIF)
if_index = *(p.i);
+#ifdef HAVE_IPV6
+ /* Solaris does not have IPV6_RECVDSTADDR AFAIK, */
+ else if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == 
IP_RECVIF)
+   {
+ addr.in6.sin6_family = AF_INET6;
+ if_index = p.s->sdl_index;
+   }
+#endif
}
 
 #elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF)
@@ -144,6 +164,9 @@
  union {
unsigned char *c;
struct in_addr *a;
+#ifdef HAVE_IPV6
+   struct in6_addr *b;
+#endif
struct sockaddr_dl *s;
  } p;
  p.c = CMSG_DATA(cmptr);
@@ -151,6 +174,18 @@
addr.in.sin_addr = *(p.a);
  else if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == 
IP_RECVIF)
if_index = p.s->sdl_index;
+#ifdef HAVE_IPV6
+ else if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == 
IPV6_RECVDSTADDR)
+   {
+ addr.in6.sin6_family = AF_INET6;
+ memcpy(&addr.in6.sin6_addr, p.b, sizeof(addr.in6.sin6_addr));
+   }
+ else if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == 
IP_RECVIF)
+   {
+ addr.in6.sin6_family = AF_INET6;
+ if_index = p.s->sdl_index;
+   }
+#endif
}
 
 #endif
@@ -172,13 +207,13 @@
   for (ir = daemon->tftp_interfaces; ir; ir = ir->next)
if (strcmp(ir->interface, name) == 0)
  break;
-   
+
   if (!ir)
{
  if (!daemon->tftp_unlimited || !check)
return;
- 
-#ifdef HAVE_DHCP  
+
+#ifdef HAVE_DHCP
  /* allowed interfaces are the same as for DHCP */
  for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
if (tmp->name && (strcmp(tmp->name, name) == 0))



Re: [Dnsmasq-discuss] TFTP for IPv6

2010-06-18 Thread Jan 'RedBully' Seiffert
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 +
+++ ipv6_tftp/src/dnsmasq.h 2010-06-18 16:23:46 +
@@ -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 +
+++ ipv6_tftp/src/network.c 2010-06-18 16:23:49 +
@@ -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 +
+++ ipv6_tftp/src/tftp.c2010-06-18 16:33:32 +
@@ -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

Re: [Dnsmasq-discuss] TFTP for IPv6

2010-06-18 Thread Simon Kelley
Ean Houts wrote:
> Hello - I'm attempting to use dnsmasq mainly for it's tftp server.  My 
> requirements are a multi-homed tftp server that serves both ipv4 and 
> ipv6 tftp clients.
> 
> I'm using fedora core 11, and the box as two NICs, both of which have 
> ipv4 and ipv6 addresses.  One of the NICs has multiple aliases with 
> distinct subnets (this is a test environment that needs to exercise alot 
> of different functionality).
> 
> Anyway, I have the dnsmasq tftp server running, but it won't respond to 
> any ipv6 addresses.  dnsmasq reports ipv6 support :
> Compile time options IPv6 GNU-getopt DBus no-I18N DHCP TFTP
> 
> And I've tried to enable this in the config - the uncommented settings 
> are :
> no-resolv
> no-poll
> enable-tftp
> tftp-root=/export/test_data/tftpboot
> tftp-no-blocksize
> conf-dir=/etc/dnsmasq.d
> 
> Unfortunately, tftp starts only listening for ipv4 :
> sudo lsof | grep tftp
> dnsmasq   24942nobody8u IPv4   11864044   
> 0t0UDP *:tftp
> 
> using the fedora core standard tftpd server, I have no problem 
> configuring it for ipv6.  Unfortunately, it doesn't seem to work very 
> well with multi-homed hosts :-(
> 
> Any pointers or tips would be greatly appreciated.
> thanks,

The TFTP server doesn't support IPv6 - my design criteria were to make
the simplest, smallest TFTP server needed to support netboot, and since
I'm not aware of any netboot infrastructure which runs over IPv6, I
didn't include that.

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.

Cheers,

Simon.



Re: [Dnsmasq-discuss] TFTP for IPv6

2010-06-18 Thread Ean Houts
Ah, ok, I'll pursue patching tftpd-hpa.
Thanks,
Ean