Fun with Flora

It’s been a while since I’ve played with AVR microcontrollers:

http://www.matthewarcus.org.uk/avr-ntp/

but I couldn’t resist getting myself an Adafruit Flora:

http://www.adafruit.com/products/659

The tutorials etc. on the Adafruit website mainly cover using Windows and Apple development platforms, though there is much useful information in their forums on using Linux – I suppose we are meant to be able to work these things out for ourselves (actually, I suspect the real reason is that there is too much variation across different distributions to be able to give a single consistent set of instructions). Anyway, this is what worked for me to get everything working nicely on Ubuntu 12.10.

Firstly, set up your IDE. The standard 1.0.4 installation is what is recommended by Adafruit, so we’ll start there (see later for 1.5.2). We need a Flora entry in the boards.txt file, and a board specific header file, to go in variants/flora, telling the compiler where the pins etc. are. These files can be extracted from the “official” Adafruit Arduino IDE for the Flora, or the excellent kkolbo has put them in a handy zip file, referenced here:

http://adafruit.com/forums/viewtopic.php?f=22&t=39012

Now we have a Flora entry on the Tools/Board menu and we can try downloading a “sketch”, as Arduino programs apparently are called. My Flora already has a blinkenlights program on it, so we’ll be wanting to change the timing or something so we can see that we really are running a new program. So, here we have the “Hello, world” of the embedded world (sketch taken directly from Adafruit tutorial):

// Pin D7 has an LED connected on FLORA.
// give it a name:
int led = 7;

// the setup routine runs once when you press reset:
void setup() {                
  // initialize the digital pin as an output.
  pinMode(led, OUTPUT);     
}

// the loop routine runs over and over again forever:
void loop() {
  digitalWrite(led, HIGH);   // turn the LED on
  delay(1500);
  digitalWrite(led, LOW);    // turn the LED off
  delay(500);
}

Nice, a little way from a Stratum-1 NTP server, but it’s a start.

To get our sketch running on the Flora, we must bear some things in mind. First thing (this held me up for a while), you upload with “Upload” and not with “Upload Using Programmer” – I suppose you only do that if you have an actual Programmer, pretty obvious really, secondly there is a “modem-manager” program, part of the Linux network manager that sometimes does strange things to the USB-pretending-to-be-a-serial-port that we are attempting to program over, and thirdly, you must, of course, have the right permissions to access said USB/serial device – for a serial over USB link, you will be looking for a device called /dev/ttyACMx and this should appear in the “Tools/Serial Ports” menu item – if no port is shown there but a suitable device exists in /dev then you probably have a permissions problem. Make sure you have the port selected too, with a tick next it. You can run the IDE as root or fiddle with group membership, but the proper way of dealing with this is with the udev device manager (which also allows us to tell modem-manager to keep its hands off).

Like the Leonardo, the Flora has one USB port, used both by the program, and for programming and it seems that the device reports different USB product ids depending on what mode it’s in – just after a reset, it reports product id 0004, then after going to “normal” mode, the product id becomes 8004 (and some of the USB properties change), and we need to set udev rules for both. (“lsusb” and “udevadm info -a -n /dev/ttyACMx” are useful here to find out what is going on).

The upshot is that we need two udev lines, for the different states of the USB connection and the different idProducts that are reported:

SUBSYSTEMS=="usb", ATTRS{idVendor}=="239a", ATTRS{idProduct}=="0004", MODE="0660", GROUP="plugdev", ENV{ID_MM_DEVICE_IGNORE}="1"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="239a", ATTRS{idProduct}=="8004", MODE="0660", GROUP="plugdev", ENV{ID_MM_DEVICE_IGNORE}="1"

The group setting ensures normal users (which Ubuntu adds to the plugdev group) can access the port, and the ID_MM_DEVICE_IGNORE tells modem-manager to ignore this device, so we no longer need to disable modem-manager while programming. There are some suggestions on the internet of using a SYMLINK+=”ttyACM%n” entry too, but that doesn’t seem to be necessary for our purposes (actually, adding “SYMLINK+=ttyUSB0” gives us a means of adding a consistent device name that is recognized by the Arduino IDE, but doesn’t clash with the kernel assigned ACM names). Add these lines into, say, /etc/udev/rules/90-flora.rules, restart udev with service udev restart and reconnect or reset your Flora. You should see something like:

$ ls -l /dev/ttyACM*
crw-rw---- 1 root plugdev 166, 1 May 18 15:59 /dev/ttyACM1

and programming should now work just fine as a normal user (assuming they are in the plugdev group). Sometimes the soft reset that the programmer does to initiate upload doesn’t work and you have to do a hard reset, but that seems to be a standard problem with Leonardo/Flora. Most of the time it seems to work just fine with the software reset.

A couple of observations: when programming, the software reset of the serial port fails if the port isn’t selected in the Serial Port menu, though programming after a hard reset will still suceed. Also sometimes if the upload fails, the avrdude process isn’t shut down properly and keeps the serial port device open, so when the USB connection is reestablished, the device minor number goes up (ie. we now have /dev/ttyACM1 rather than /dev/ttyACM0). If this happens, “killall avrdude” will restore the status quo.

Finally, I had originally tried using an Arduino 1.5.2 installation I set up when I was thinking of having a play with the ARM-based Arduino Due. Initially this didn’t work too well for the Flora, but after sorting out the USB device problems as above and copying the relevant seeming lines from the Leonardo section of the boards.txt file to the Flora section (and copying the variants/flora directory), it was just fine:

flora.name=Adafruit Flora
flora.upload.tool=avrdude
flora.upload.protocol=avr109
flora.upload.maximum_size=28672
flora.upload.speed=57600
flora.upload.disable_flushing=true
flora.upload.use_1200bps_touch=true
flora.upload.wait_for_upload_port=true
flora.bootloader.tool=avrdude
flora.bootloader.low_fuses=0xff
flora.bootloader.high_fuses=0xd8
flora.bootloader.extended_fuses=0xcb
flora.bootloader.path=caterina
flora.bootloader.file=Caterina-Flora.hex
flora.bootloader.unlock_bits=0x3F
flora.bootloader.lock_bits=0x2F
flora.build.mcu=atmega32u4
flora.build.f_cpu=8000000L
flora.build.vid=0x239A
flora.build.pid=0x8004
flora.build.core=arduino
flora.build.variant=flora
flora.build.extra_flags=-DUSB_VID={build.vid} -DUSB_PID={build.pid}

(I’m not sure that I’ve got that bootloader, but I don’t need it at the moment).

Anyway, a nice bit of kit, good work from Limor Fried and all at Adafruit, and now I can make the LED flash just as I like, time to think of some more interesting applications. Watch this space…

Advertisements

Fun with TUN

TUN devices are much used for virtualization, VPNs, network testing programs, etc. A TUN device essentially is a network interface that also exists as a user space file descriptor, data sent to the interface can be read from the file descriptor, and data written to the file descriptor emerges from the network interface.

Here’s a simple example of their use. We create a TUN device that simulates an entire network, with traffic to each network address just routed back to the original host.

For a complete program, see:

https://github.com/matthewarcus/stuff/blob/master/tun/reflect.cpp

First create your TUN device, this is fairly standard, most public code seems to be derived from Maxim Krasnyansky’s:

https://www.kernel.org/doc/Documentation/networking/tuntap.txt

and our code is no different:

int tun_alloc(char *dev) 
{
  assert(dev != NULL);
  int fd = open("/dev/net/tun", O_RDWR);
  CHECKFD(fd);

  struct ifreq ifr; 
  memset(&ifr, 0, sizeof(ifr)); 
  ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
  strncpy(ifr.ifr_name, dev, IFNAMSIZ); 
  CHECKSYS(ioctl(fd, TUNSETIFF, (void *) &ifr));
  strncpy(dev, ifr.ifr_name, IFNAMSIZ); 
  return fd;
}

We want a TUN device (rather than TAP, essentially the same thing but at the ethernet level) and we don’t want packet information at the moment. We copy the name of the allocated device to the char array given as a parameter.

Now all our program needs to do is create the TUN device and sit in a loop copying packets:

int main(int argc, char *argv[])
{
  char dev[IFNAMSIZ+1];
  memset(dev,0,sizeof(dev));
  if (argc > 1) strncpy(dev,argv[1],sizeof(dev)-1);

  // Allocate the tun device
  int fd = tun_alloc(dev);
  if (fd < 0) exit(0);

  uint8_t buf[2048];
  while(true) {
    // Sit in a loop, read a packet from fd, reflect
    // addresses and write back to fd.
    ssize_t nread = read(fd,buf,sizeof(buf));
    CHECK(nread >= 0);
    if (nread == 0) break;
    reflect(buf,nread);
    ssize_t nwrite = write(fd,buf,nread);
    CHECK(nwrite == nread);
  }
}

The TUN mechanism ensures that we get exactly one packet for each read, we don’t need to worry about fragmentation, and we just send each packet back with the source and destination IPs swapped:

static inline void put32(uint8_t *p, size_t offset, uint32_t n)
{
  memcpy(p+offset,&n,sizeof(n));
}

static inline uint32_t get32(uint8_t *p, size_t offset)
{
  uint32_t n;
  memcpy(&n,p+offset,sizeof(n));
  return n;
}

void reflect(uint8_t *p, size_t nbytes)
{
  (void)nbytes;
  uint8_t version = p[0] >> 4;
  switch (version) {
  case 4:
    break;
  case 6:
    fprintf(stderr, "IPv6 not implemented yet\n");
    exit(0);
  default:
    fprintf(stderr, "Unknown protocol %u\n", version);
    exit(0);
  }
  uint32_t src = get32(p,12);
  uint32_t dst = get32(p,16);
  put32(p,12,dst);
  put32(p,16,src);
}

We don’t need to recalculate the header checksum as it doesn’t get changed by just swapping two 32 bit segments.

Handling IPV6 is left as an exercise for the reader (we just need to use a different offset and address size I think).

In this day and age, security should be prominent in our minds, particularly for long-running programs like our TUN server, so for extra points, let’s add in some capability processing.

(You might need to install a libcap-dev package for this to work, for example, with “sudo apt-get install libcap-dev” and link with -lcap).

Once we have started up, we should check if we have the required capability, we just require CAP_NET_ADMIN to be permitted:

  cap_t caps = cap_get_proc();
  CHECK(caps != NULL);

  cap_value_t cap = CAP_NET_ADMIN;
  const char *capname = STRING(CAP_NET_ADMIN);

  cap_flag_value_t cap_permitted;
  CHECKSYS(cap_get_flag(caps, cap,
                        CAP_PERMITTED, &cap_permitted));
  if (!cap_permitted) {
    fprintf(stderr, "%s not permitted, exiting\n", capname);
    exit(0);
  }

and then make effective what we require:

  CHECKSYS(cap_clear(caps));
  CHECKSYS(cap_set_flag(caps, CAP_PERMITTED, 1, &cap, CAP_SET));
  CHECKSYS(cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap, CAP_SET));
  CHECKSYS(cap_set_proc(caps));

Finally, after creating our TUN object, before entering our main loop, we can relinquish our extra privileges altogether:

  CHECKSYS(cap_clear(caps));
  CHECKSYS(cap_set_proc(caps));
  CHECKSYS(cap_free(caps));

For completeness, here are the error checking macros used above:

#define CHECKAUX(e,s)                            \
 ((e)? \
  (void)0: \
  (fprintf(stderr, "'%s' failed at %s:%d - %s\n", \
           s, __FILE__, __LINE__,strerror(errno)), \
   exit(0)))
#define CHECK(e) (CHECKAUX(e,#e))
#define CHECKSYS(e) (CHECKAUX((e)==0,#e))
#define CHECKFD(e) (CHECKAUX((e)>=0,#e))
#define STRING(e) #e

Of course, production code will want to do something more sophisticated than calling exit(0) when an error occurs…

To use, compile for example with:

g++ -W -Wall -O3 reflect.cpp -lcap -o reflect

We can set permissions for our new executable to include the relevant capability, so we don’t need to start it as root:

$ sudo setcap cap_net_admin+ep ./reflect

Actually start it:

$ ./reflect&
Capability CAP_NET_ADMIN: 1 0 1
Created tun device tun0

We now have an interface, but it isn’t configured:

$ ifconfig tun0
tun0 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
POINTOPOINT NOARP MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:500
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)

With the interface running, set up networking:

$ sudo ip link set tun0 up
$ sudo ip addr add 10.0.0.1/8 dev tun0

Check all is well:

$ ifconfig tun0
tun0 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
inet addr:10.0.0.1 P-t-P:10.0.0.1 Mask:255.0.0.0
UP POINTOPOINT RUNNING NOARP MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:500
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)

And try it out:

$ ping -c 1 10.0.0.41
PING 10.0.0.41 (10.0.0.41) 56(84) bytes of data.
64 bytes from 10.0.0.41: icmp_req=1 ttl=64 time=0.052 ms

--- 10.0.0.41 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.052/0.052/0.052/0.000 ms

Let’s check performance, firstly, a flood ping on the loopback device:

$ sudo ping -f -c10000 -s1500 127.0.0.1
PING 127.0.0.1 (127.0.0.1) 1500(1528) bytes of data.

--- 127.0.0.1 ping statistics ---
10000 packets transmitted, 10000 received, 0% packet loss, time 778ms
rtt min/avg/max/mdev = 0.003/0.006/0.044/0.002 ms, pipe 2, ipg/ewma 0.077/0.006 ms

compared to one through the TUN connection:

$ sudo ping -f -c10000 -s1500 10.0.0.100
PING 10.0.0.100 (10.0.0.100) 1500(1528) bytes of data.

--- 10.0.0.100 ping statistics ---
10000 packets transmitted, 10000 received, 0% packet loss, time 945ms
rtt min/avg/max/mdev = 0.022/0.032/3.775/0.038 ms, pipe 2, ipg/ewma 0.094/0.032 ms

Respectable. We have got ourselves a network!