Subject: pkgsrc/wakeup with multiple interfaces
To: None <current-users@netbsd.org>
From: John F <from_netbsd5@frear.com>
List: current-users
Date: 12/12/2005 00:18:35
Hi all!
I'm trying to figure out how to make a C program send out a udp
packet to the broadcast address and select which interface is used.
I'm trying to modify 'hpwake.c' from the wakeup pkgsrc package
because I noticed that hpwake.c has no mechanism to allow you to select
which interface is used to transmit the wake-on-lan "magic packet". As
shipped, the To: address is INADDR_BROADCAST and the From: address is
INADDR_ANY.
I want to do this because if you have a multihomed system it seems
that the program selects an arbitrary interface to send its udp packet
down. My neb 1.6.2 system has an rtk and an fxp and the default gateway
is reached via the rtk. The 'wakeup' program has always sent its magic
packet down the fxp, which is exactly what I want. One day, I modified
the netmask of the rtk and 'wakeup' decided to send its packets down that
interface instead. A reboot of the system (while keeping the updated
netmask) caused 'wakeup' to revert to its orignal behavior of using the
fxp. So I decided that I have no clue how the program decided to use the
fxp and also that 'wakeup' needs a command line option to allow you to
select the interface that it uses.
I decided to use the getifaddrs function and have had two
approaches at this.
My first try was to change the From: address to be the IP of a
local interface. I had high hopes for this method because lsof showed the
udp socket attached to the correct IP address after the bind call. But,
it didn't work - packets always left via the fxp but they were labeled
with the IP of the fxp, the rtk, or the loopback interface, depending on
which interface I gave on the command line (that is, which local address
was passed to the bind() call).
My second try was based on the idea that, IP routing is based on
To: addresses, not From:. So, instead of modifying the packet source, I
pulled the broadcast field from the getifaddrs result and used that as my
destination. This resulted in the same behavior as the stock program had.
So, that's the problem I'm trying to solve and the approach I've
taken. I'm very new to socket programming in C, so I'm still working at
it and perhaps I may solve this yet. I just want to share my efforts and
see if anyone sees the flaw in my approach or my code. Your comments are
greatly appriceated.
Thank you muchly,
- John F.
from_netbsd5@frear.com
-- The patch for try #1
--- hpwake.c Wed Dec 7 00:05:07 2005
+++ hpwake.c.new Sun Dec 11 20:19:06 2005
@@ -25,11 +25,68 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
+#include <ifaddrs.h>
+#include <net/if.h>
+#include <arpa/inet.h>
u_char magicpacket[500];
int get_magicpacket(unsigned char *,char *);
int main(int,char *[]);
+int select_if( char *, struct sockaddr_in *);
+void usage(char *);
+
+void
+usage(name)
+
+char *name;
+
+{
+ fprintf(stderr, "Usage: %s [-i ifname] xx:xx:xx:xx:xx:xx\n", name);
+ exit(1);
+}
+
+int
+select_if(if_name,sa)
+
+char *if_name;
+struct sockaddr_in *sa;
+
+{
+ struct ifaddrs *ifaddrs, *cur_if;
+ int matched = 0;
+
+ if( getifaddrs(&ifaddrs) != 0 ) {
+ perror( "getifaddrs" );
+ return NULL;
+ }
+ for( cur_if=ifaddrs; cur_if != NULL; cur_if=cur_if->ifa_next ) {
+ if( (cur_if->ifa_flags & IFF_UP) &&
+ (cur_if->ifa_addr->sa_family == PF_INET) &&
+ strcmp( cur_if->ifa_name, if_name ) == 0 )
+ {
+ matched = 1;
+ memcpy(sa, cur_if->ifa_addr, sizeof(struct sockaddr));
+ break;
+ }
+ }
+ freeifaddrs( ifaddrs );
+ if( !matched )
+ {
+ fprintf(stderr, "Fatal: Couldn't find interface %s.\n",if_name);
+ return NULL;
+ }
+ printf("Using interface %s, %s\n", if_name, inet_ntoa(sa->sin_addr) );
+ /*
+ printf("sa.sin_len=%d,sa.sin_family=%s,sa.sin_port=%d,sa.sin_addr=%s\n",
+ sa->sin_len,
+ sa->sin_family == PF_INET ? "PF_INET" : "Not pf_inet",
+ sa->sin_port,
+ inet_ntoa( sa->sin_addr ) );
+ */
+
+ return 1;
+}
int get_magicpacket(packet,arg)
@@ -101,13 +158,26 @@
int packetsize;
int s;
int i;
+ int doselect_if;
- if (argc < 2) {
- fprintf(stderr, "Usage: %s xx:xx:xx:xx:xx:xx\n", argv[0]);
- exit(1);
+ switch( argc )
+ {
+ case 2:
+ doselect_if = 0;
+ break;
+ case 4:
+ if( strcmp( argv[1], "-i" ) == 0 ) {
+ doselect_if = 1;
+ break;
+ }
+ default:
+ usage(argv[0]);
}
- packetsize = get_magicpacket(magicpacket, argv[1]);
+ if( doselect_if )
+ packetsize = get_magicpacket(magicpacket, argv[3]);
+ else
+ packetsize = get_magicpacket(magicpacket, argv[1]);
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
perror("socket");
@@ -115,9 +185,15 @@
if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char *)&one, sizeof(one)) < 0)
perror("setsockopt");
- server.sin_family = AF_INET;
- server.sin_port = 0;
- server.sin_addr.s_addr = INADDR_ANY;
+ if( doselect_if ) {
+ if( select_if( argv[2], &server ) == NULL )
+ return 1;
+ server.sin_port = 0;
+ } else {
+ server.sin_family = AF_INET;
+ server.sin_port = 0;
+ server.sin_addr.s_addr = INADDR_ANY;
+ }
client.sin_family = AF_INET;
client.sin_port = 32768+666; /* invalid port */
-- Try #2. This patch was generated from try #1 not from the orig hpwake.c
--- try1/hpwake.c Sun Dec 11 20:19:06 2005
+++ try2/hpwake.c Sun Dec 11 23:37:42 2005
@@ -25,8 +25,8 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
-#include <ifaddrs.h>
#include <net/if.h>
+#include <ifaddrs.h>
#include <arpa/inet.h>
u_char magicpacket[500];
@@ -62,11 +62,12 @@
}
for( cur_if=ifaddrs; cur_if != NULL; cur_if=cur_if->ifa_next ) {
if( (cur_if->ifa_flags & IFF_UP) &&
- (cur_if->ifa_addr->sa_family == PF_INET) &&
+ (cur_if->ifa_broadaddr != NULL) &&
+ (cur_if->ifa_broadaddr->sa_family == PF_INET) &&
strcmp( cur_if->ifa_name, if_name ) == 0 )
{
matched = 1;
- memcpy(sa, cur_if->ifa_addr, sizeof(struct sockaddr));
+ memcpy(sa, cur_if->ifa_broadaddr, sizeof(struct sockaddr));
break;
}
}
@@ -77,13 +78,11 @@
return NULL;
}
printf("Using interface %s, %s\n", if_name, inet_ntoa(sa->sin_addr) );
- /*
printf("sa.sin_len=%d,sa.sin_family=%s,sa.sin_port=%d,sa.sin_addr=%s\n",
sa->sin_len,
sa->sin_family == PF_INET ? "PF_INET" : "Not pf_inet",
sa->sin_port,
inet_ntoa( sa->sin_addr ) );
- */
return 1;
}
@@ -186,18 +185,18 @@
perror("setsockopt");
if( doselect_if ) {
- if( select_if( argv[2], &server ) == NULL )
+ if( select_if( argv[2], &client ) == NULL )
return 1;
- server.sin_port = 0;
+ client.sin_port = 32768+666; /* invalid port */
} else {
- server.sin_family = AF_INET;
- server.sin_port = 0;
- server.sin_addr.s_addr = INADDR_ANY;
+ client.sin_family = AF_INET;
+ client.sin_port = 32768+666; /* invalid port */
+ client.sin_addr.s_addr = INADDR_BROADCAST;
}
- client.sin_family = AF_INET;
- client.sin_port = 32768+666; /* invalid port */
- client.sin_addr.s_addr = INADDR_BROADCAST;
+ server.sin_family = AF_INET;
+ server.sin_port = 0;
+ server.sin_addr.s_addr = INADDR_ANY;
if (bind(s, (struct sockaddr *)&server, sizeof(server)) < 0)
perror("bind");