mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
336 lines
7.8 KiB
C
336 lines
7.8 KiB
C
/*
|
|
* Copyright (C) 2014 Freie Universität Berlin.
|
|
*
|
|
* This file is subject to the terms and conditions of the GNU Lesser
|
|
* General Public License v2.1. See the file LICENSE in the top level
|
|
* directory for more details.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <pthread.h>
|
|
#include <signal.h>
|
|
|
|
#include <arpa/inet.h>
|
|
#include <net/if.h>
|
|
#include <netinet/ip6.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
#include <sys/types.h>
|
|
|
|
#include <linux/if_tun.h>
|
|
|
|
#include "sixlowdriver.h"
|
|
#include "multiplex.h"
|
|
#include "flowcontrol.h"
|
|
#include "serialnumber.h"
|
|
#include "control_2xxx.h"
|
|
|
|
#define TUNDEV "/dev/net/tun"
|
|
#define MAXIMUM_CONTEXTS 16
|
|
|
|
char tun_if_name[IF_NAME_LEN];
|
|
|
|
uint8_t tun_out_buf[BUFFER_SIZE];
|
|
uint8_t tun_in_buf[BUFFER_SIZE];
|
|
|
|
/* Cell i is defined as empty if context_cache[i].cid != i */
|
|
border_context_t context_cache[MAXIMUM_CONTEXTS];
|
|
|
|
int tun_fd;
|
|
pthread_t serial_reader, tun_reader;
|
|
uint16_t abro_version = 0;
|
|
|
|
uint16_t get_abro_version()
|
|
{
|
|
return abro_version;
|
|
}
|
|
|
|
uint16_t get_next_abro_version()
|
|
{
|
|
abro_version = serial_add16(abro_version, 1);
|
|
return abro_version;
|
|
}
|
|
|
|
int get_tun_fd(void)
|
|
{
|
|
return tun_fd;
|
|
}
|
|
|
|
void *serial_reader_f(void *arg)
|
|
{
|
|
unsigned char buf[BUFFER_SIZE];
|
|
border_packet_t *packet_buf;
|
|
|
|
while (1) {
|
|
int n = readpacket(buf, BUFFER_SIZE);
|
|
|
|
if (n > 0) {
|
|
if (buf[0] == 0) {
|
|
packet_buf = (border_packet_t *)buf;
|
|
flowcontrol_deliver_from_tty(packet_buf, n);
|
|
continue;
|
|
}
|
|
|
|
printf("\033[00;33m[via serial interface] %s\033[00m\n", buf);
|
|
}
|
|
}
|
|
}
|
|
|
|
int tun_to_serial_packet(uint8_t *serial_packet, uint8_t *tun_packet, size_t packet_size)
|
|
{
|
|
struct tun_pi *tun_hdr = (struct tun_pi *)tun_packet;
|
|
border_l3_header_t *l3_hdr = (border_l3_header_t *)serial_packet;
|
|
|
|
l3_hdr->empty = 0;
|
|
l3_hdr->type = BORDER_PACKET_L3_TYPE;
|
|
l3_hdr->ethertype = ntohs(tun_hdr->proto);
|
|
|
|
memcpy(
|
|
serial_packet + sizeof(border_l3_header_t),
|
|
tun_packet + sizeof(struct tun_pi),
|
|
packet_size - sizeof(struct tun_pi)
|
|
);
|
|
return (sizeof(border_l3_header_t) + (packet_size - sizeof(struct tun_pi)));
|
|
}
|
|
|
|
void *tun_reader_f(void *args)
|
|
{
|
|
unsigned char data[BUFFER_SIZE];
|
|
size_t bytes;
|
|
|
|
while (1) {
|
|
bytes = read(tun_fd, (void *)data, BUFFER_SIZE);
|
|
|
|
if (bytes > 0) {
|
|
bytes = tun_to_serial_packet(tun_in_buf, (uint8_t *)data, bytes);
|
|
flowcontrol_send_over_tty((border_packet_t *)tun_in_buf, bytes);
|
|
}
|
|
}
|
|
}
|
|
|
|
void border_send_ipv6_over_tun(int fd, const struct ip6_hdr *packet)
|
|
{
|
|
uint8_t tun_packet[BUFFER_SIZE];
|
|
int packet_size = packet->ip6_plen + sizeof(struct ip6_hdr);
|
|
|
|
struct tun_pi *tun_hdr = (struct tun_pi *)tun_packet;
|
|
|
|
tun_hdr->flags = 0;
|
|
tun_hdr->proto = htons(ETHERTYPE_IPV6);
|
|
|
|
memcpy(tun_packet + sizeof(struct tun_pi), (uint8_t *)packet, packet_size);
|
|
write(tun_fd, tun_packet, packet_size + sizeof(struct tun_pi));
|
|
}
|
|
|
|
int tun_set_owner(int fd, const uid_t *uid, const gid_t *gid)
|
|
{
|
|
if (uid != NULL) {
|
|
if (*uid != -1 && ioctl(fd, TUNSETOWNER, *uid)) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (gid != NULL) {
|
|
if (*gid != -1 && ioctl(fd, TUNSETGROUP, *gid)) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int tun_add_addr(const char *ip_addr)
|
|
{
|
|
char command[21 + IPV6_ADDR_LEN + IF_NAME_LEN];
|
|
|
|
printf("INFO: ip addr add %s dev %s\n", ip_addr, tun_if_name);
|
|
sprintf(command, "ip addr add %s dev %s", ip_addr, tun_if_name);
|
|
|
|
if (system(command) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Source: http://backreference.org/2010/03/26/tuntap-interface-tutorial/
|
|
*/
|
|
int open_tun(char *if_name, int flags)
|
|
{
|
|
struct ifreq ifr;
|
|
int fd, err;
|
|
|
|
/* Arguments taken by the function:
|
|
*
|
|
* char *if_name: the name of an interface (or '\0'). MUST have enough
|
|
* space to hold the interface name if '\0' is passed
|
|
* int flags: interface flags (eg, IFF_TUN etc.)
|
|
*/
|
|
|
|
/* open the clone device */
|
|
if ((fd = open(TUNDEV, O_RDWR)) < 0) {
|
|
return fd;
|
|
}
|
|
|
|
/* preparation of the struct ifr, of type "struct ifreq" */
|
|
memset(&ifr, 0, sizeof(ifr));
|
|
|
|
ifr.ifr_flags = flags; /* IFF_TUN or IFF_TAP, plus maybe IFF_NO_PI */
|
|
|
|
if (*if_name) {
|
|
/* if a device name was specified, put it in the structure; otherwise,
|
|
* the kernel will try to allocate the "next" device of the
|
|
* specified type */
|
|
strncpy(ifr.ifr_name, if_name, IFNAMSIZ);
|
|
}
|
|
|
|
/* try to create the device */
|
|
if ((err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0) {
|
|
close(fd);
|
|
return err;
|
|
}
|
|
|
|
/* if the operation was successful, write back the name of the
|
|
* interface to the variable "if_name", so the caller can know
|
|
* it. Note that the caller MUST reserve space in *if_name (see calling
|
|
* code below) */
|
|
strcpy(if_name, ifr.ifr_name);
|
|
|
|
/* this is the special file descriptor that the caller will use to talk
|
|
* with the virtual interface */
|
|
return fd;
|
|
}
|
|
|
|
int context_empty(uint8_t cid)
|
|
{
|
|
return context_cache[cid].cid != cid;
|
|
}
|
|
|
|
int border_update_context(uint8_t cid, const struct in6_addr *prefix,
|
|
uint8_t len, uint8_t comp,
|
|
uint16_t lifetime)
|
|
{
|
|
if (cid >= MAXIMUM_CONTEXTS) {
|
|
return -1;
|
|
}
|
|
|
|
len = (len <= 128) ? len : 128;
|
|
|
|
if (context_empty(cid)) {
|
|
context_cache[cid].version = get_abro_version();
|
|
}
|
|
else {
|
|
context_cache[cid].version = get_next_abro_version();
|
|
}
|
|
|
|
context_cache[cid].cid = cid;
|
|
memcpy(&(context_cache[cid].prefix), prefix, len);
|
|
context_cache[cid].len = len;
|
|
context_cache[cid].comp = comp;
|
|
context_cache[cid].lifetime = lifetime;
|
|
|
|
multiplex_send_context_over_tty(&context_cache[cid]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int border_renew_existing_context(uint8_t cid)
|
|
{
|
|
if (cid >= MAXIMUM_CONTEXTS) {
|
|
return -1;
|
|
}
|
|
|
|
if (context_empty(cid)) {
|
|
return -1;
|
|
}
|
|
|
|
multiplex_send_context_over_tty(&context_cache[cid]);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void border_remove_context(uint8_t cid)
|
|
{
|
|
if (cid >= MAXIMUM_CONTEXTS) {
|
|
return;
|
|
}
|
|
|
|
if (context_empty(cid)) {
|
|
return;
|
|
}
|
|
|
|
context_cache[cid].version = get_next_abro_version();
|
|
context_cache[cid].lifetime = 0;
|
|
|
|
multiplex_send_context_over_tty(&context_cache[cid]);
|
|
|
|
context_cache[cid].cid = 0xFF;
|
|
}
|
|
|
|
int border_add_addr(const char *ip_addr)
|
|
{
|
|
struct in6_addr parsed_addr;
|
|
|
|
if (inet_pton(AF_INET6, ip_addr, &parsed_addr) != 1) {
|
|
return -1;
|
|
}
|
|
|
|
tun_add_addr(ip_addr);
|
|
|
|
multiplex_send_addr_over_tty(&parsed_addr);
|
|
return 0;
|
|
}
|
|
|
|
int border_initialize(char *if_name, const char *ip_addr, const char *tty_dev)
|
|
{
|
|
int res, i;
|
|
char command[21 + IPV6_ADDR_LEN + IF_NAME_LEN];
|
|
char ip_addr_cpy[IPV6_ADDR_LEN];
|
|
struct in6_addr parsed_addr;
|
|
|
|
strncpy(ip_addr_cpy, ip_addr, IPV6_ADDR_LEN);
|
|
|
|
strtok(ip_addr_cpy, "/");
|
|
|
|
if ((res = inet_pton(AF_INET6, ip_addr_cpy, &parsed_addr)) != 1) {
|
|
return res;
|
|
}
|
|
|
|
if ((res = init_multiplex(tty_dev)) != 0) {
|
|
return res;
|
|
}
|
|
|
|
tun_fd = open_tun(if_name, IFF_TUN);
|
|
|
|
printf("INFO: ip link set %s up\n", if_name);
|
|
sprintf(command, "ip link set %s up", if_name);
|
|
|
|
strncpy(tun_if_name, if_name, IF_NAME_LEN);
|
|
|
|
if ((res = system(command)) != 0) {
|
|
return res;
|
|
}
|
|
|
|
if ((res = tun_add_addr(ip_addr)) != 0) {
|
|
return res;
|
|
}
|
|
|
|
// initialize context cache as empty.
|
|
for (i = 0; i < MAXIMUM_CONTEXTS; i++) {
|
|
context_cache[i].cid = 0xFF;
|
|
}
|
|
|
|
pthread_create(&serial_reader, NULL, serial_reader_f, NULL);
|
|
|
|
hard_reset_to_user_code();
|
|
flowcontrol_init(&parsed_addr);
|
|
pthread_create(&tun_reader, NULL, tun_reader_f, NULL);
|
|
|
|
return 0;
|
|
}
|