Well, over the past couple of weeks I have been working on the begginings of my MPLS implementation. The first step was to write a program that can inject semi-customisable MPLS packets into the FreeBSD networking stack. I found when I started this project there was very few resources that I could find that helped me complete this. So, in an effort to help anyone else attempting to write an injection tool I figured I might write a short post (well as short as I can keep it) on how to acheive this. I’ll try and keep it brief.
The first step is to create the datagram packet that will be sent.
uint8_t datagram[1500]
where the size of the array is the number of bytes you are wanting to send.
Once the datagram is created you need to fill it with the packets header values. Most types of headers you will already find defined inside the /src/netinet/ folder. These are normally very easy to use. They are created as follows:
struct ether_header *eth = (struct ether_header *)(datagram);
struct ip *iph = (struct ip *)(datagram + sizeof(uint32_t) + sizeof(struct ether_header));
struct icmphdr *icmph = (struct icmphdr *)(datagram + sizeof(struct ip) + sizeof(struct ether_header));
In this example the struct is defined, with a pointer which also points to the point in the datagram where the header needs to start. For instance, the icmp header needs to point to the next byte after the IP header, which is the size of the ip header + the size of the ethernet header, past the start of the datagram. Once these have been created it’s fairly simple to assign values to the headers, like so:
iph->ip_hl = 5;
iph->ip_v = 4;
iph->ip_tos = ip4_tos;
iph->ip_len = htons((iph->ip_hl<<2) + 8 + 20);
I’ll leave it up to you to figure out the rest of the values, which is pretty easy if you look at the struct definitions.
If you require a header that is not already defined in the netinet package, the easiest way I found to do this was to create a int larger than the size of the header, then once it has had the values inserted into it, use byte shifting to make the header values lined up correctly. Then the values are copied into the datagram using memcpy.
uint32_t mplsh;
mplsh = htonl(mpls_label << 12 | mpls_qos << 9 | mpls_bos_flag << 8 | mpls_ttl);
memcpy(&datagram[14], &mplsh, 4);
After this the packet has been created and is ready to send along the wire. To do this we use a BPF device. So, we need to find a spare BPF device, open it up, and writing the packet to it.
To find a spare device, you basically have to poll through every possible BPF device until you find one that isnt being used.
int
dl_bpf_open_dev(char *dev, const size_t len)
{
int i=0, fd;
do {
snprintf(dev, len, "/dev/bpf%d", i);
if ((fd = open(dev, O_RDWR)) == -1) {
if (errno == EBUSY)
continue;
else {
fprintf(stderr, "could not open %s\n", dev);
return -1;
}
}
else break;
}
while (++i < 32768);
return fd;
}
Once this is done you need to open the device to allow you to write to it.
int
dl_bpf_open(const char *ifname)
{
struct ifreq ifreq;
char dev[16];
int fd;
memset(&ifreq, 0, sizeof(ifreq));
strcpy(ifreq.ifr_name, ifname);
if ((fd = dl_bpf_open_dev(dev, sizeof(dev))) == -1)
return -1;
if (ioctl(fd, BIOCSETIF, &ifreq) == -1) {
fprintf(stderr, "%s BIOCSETIF %s failed\n", dev, ifreq.ifr_name);
close(fd);
return -1;
}
return fd;
}
Note: Thank you to Matthew Luckie for giving me the code to find and open the device. ALso, if you wish to use this code, then all you need to do is make a call to dl_bpf_open() and it will do the rest for you.
Once this is done you simply need to write to the open device, then close it up and you’re all done.
if ((wb = write(fd, datagram, len)) < (ssize_t)len) {
if (wb == -1)
fprintf(stderr, "%d bytes failed\n", len);
else
printf("%d bytes sent of %d total\n", wb, len);
return -1;
}
So thats basically it. I dont think I’ve gone quite as indepth as I possibly could, but from this most people should be able to get something working. If anyone has anymore questions post them at either of my websites and I’ll get back to you. Also, if anyone out there notices a mistake I’ve made, please let me know asap, before I put too many people wrong.
EDIT: I’m having a few problems with keeping tabs and spacing formatting in the code sections, if anyone knows how to fix this please let me know.