Tuesday, August 15, 2006

amazingly difficult to find 'getting a MAC address from a interface?' answer.

I was trying to convert a piece of network software to FreeBSD. Unfortunately, even though the author quotes
"should compile on any POSIX like OS," it doesn't (POSIX != LINUX). It should be obvious, when they drop things like:


if (ioctl (sock, SIOCGIFADDR, &ifr) < 0)


throughout the code.

So the above is from a section of code trying to obtain the MAC address on a interface. Doing this task
on FreeBSD can be done through ioctl(), but is discouraged by the developers. They encourage you to use getifaddrs().

This strange and wonderful function grabs all sorts of information from the interface list, however, a couple of things
are not obvious about it's use. If you really want to play arround with IF_LINK type addresses on the interfaces returned
you need to be using sockaddr_dl not just sockaddr. Overloading sockaddr with magic casting is the new pink these days and
thats what's required. It confused me at first since the manpage has:


struct ifaddrs *ifa_next; /* Pointer to next struct */
char *ifa_name; /* Interface name */
u_int ifa_flags; /* Interface flags */
struct sockaddr *ifa_addr; /* Interface address */
struct sockaddr *ifa_netmask; /* Interface netmask */
struct sockaddr *ifa_broadaddr; /* Interface broadcast address */
struct sockaddr *ifa_dstaddr; /* P2P interface destination */
void *ifa_data; /* Address specific data */


for the structure.. same with /usr/include/ifaddrs.h. It turns out copying sa_data of sa_len from your friendly
struct sockaddr does not give your the ethernet address from ifa_addr. :( :( :( :(.

Too make a long story short:


/* getmac.c -- retrieve the mac address from a interface name */
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if_dl.h>
#include <ifaddrs.h>

int main(int argc, char ** argv)
{
struct ifaddrs *ifaphead;
unsigned char * if_mac;
int found = 0;
struct ifaddrs *ifap;
struct sockaddr_dl *sdl = NULL;

if (argc < 2)
{
fprintf(stderr,"usage:\n");
fprintf(stderr,"\t %s <interface name>\n",argv[0]);
exit(1);
}

if (getifaddrs(&ifaphead) != 0)
{
perror("get_if_name: getifaddrs() failed");
exit(1);
}

for (ifap = ifaphead; ifap && !found; ifap = ifap->ifa_next)
{
if ((ifap->ifa_addr->sa_family == AF_LINK))
{
if (strlen(ifap->ifa_name) == strlen(argv[1]))
if (strcmp(ifap->ifa_name,argv[1]) == 0)
{
found = 1;
sdl = (struct sockaddr_dl *)ifap->ifa_addr;
if (sdl)
{
/* I was returning this from a function before converting
* this snippet, which is why I make a copy here on the heap */
if_mac = malloc(sdl->sdl_alen);
memcpy(if_mac, LLADDR(sdl), sdl->sdl_alen);
}
}
}
}
if (!found)
{
fprintf (stderr,"Can't find interface %s.\n",argv[1]);
if(ifaphead)
freeifaddrs(ifaphead);
exit(1);
}

fprintf (stdout, "%02X%02X%02X%02X%02X%02X\n",
if_mac[0] , if_mac[1] , if_mac[2] ,
if_mac[3] , if_mac[4] , if_mac[5] );

if(ifaphead)
freeifaddrs(ifaphead);

exit(0);
}

Wednesday, August 9, 2006

command line new tabs in konqueror

The following is a simple shell script using the KDE dcop client to open new tabs in konqueror (or a new instance of konqueror if it's not running). I use this from pine's url-viewers setting to launch new tabs with a url contained in a email.


#!/bin/sh

KONQ_WIN=`dcop konq* | head -1`

if [ "$KONQ_WIN" = "" ]; then
/usr/local/bin/konqueror $1 &
else
NEW_TAB=`dcop $KONQ_WIN konqueror-mainwindow#1 action newtab`
dcop $NEW_TAB activate
dcop $KONQ_WIN konqueror-mainwindow#1 openURL $1
fi
return 0