diff --git a/.gitignore b/.gitignore index e8dd647..b7015c3 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ targetConfigs/ setenv .DS_Store bin/ +dist/tools/vaina/target diff --git a/Kconfig b/Kconfig index f386c93..63bcfc1 100644 --- a/Kconfig +++ b/Kconfig @@ -2,6 +2,12 @@ menu "Turpial - Radio Firmware Configuration" +config SLIP_LOCAL_ADDR + string "SLIP link local address" + default "fe80::dead:beef:cafe:babe" + help + Default SLIP link local address + rsource "sys/Kconfig" endmenu diff --git a/Makefile b/Makefile index f88d06a..0bab992 100644 --- a/Makefile +++ b/Makefile @@ -3,9 +3,7 @@ APPLICATION = radio-firmware # If no BOARD is found in the environment, use this default: BOARD ?= turpial -ifeq ($(BOARD),turpial) - BOARDSDIR ?= $(CURDIR)/boards -endif +EXTERNAL_BOARD_DIRS ?= $(CURDIR)/boards # This has to be the absolute path to the RIOT base directory: RIOTBASE ?= $(CURDIR)/RIOT @@ -42,20 +40,45 @@ USEMODULE += ps USEMODULE += netstats_l2 USEMODULE += netstats_ipv6 +USEMODULE += vaina + USEMODULE += posix_inet USEMODULE += timex USEMODULE += slipdev +USEMODULE += slipdev_stdio # set slip parameters to default values if unset -SLIP_UART ?= "UART_NUMOF-1" +ifeq ($(BOARD),cc2538dk) + SLIP_UART ?= "UART_NUMOF-1" +else + SLIP_UART ?= "0" +endif SLIP_BAUDRATE ?= 115200 # export slip parameters CFLAGS += -DSLIP_UART="UART_DEV($(SLIP_UART))" CFLAGS += -DSLIP_BAUDRATE=$(SLIP_BAUDRATE) +# Enable SLAAC +CFLAGS += -DCONFIG_GNRC_IPV6_NIB_SLAAC=1 + CFLAGS += -I$(CURDIR) +.PHONY: host-tools + +host-tools: + $(Q)env -u CC -u CFLAGS make -C $(RIOTTOOLS) + +sliptty: + $(Q)env -u CC -u CFLAGS make -C $(RIOTTOOLS)/sliptty + +IPV6_PREFIX = 2001:db8::/64 + +# Configure terminal parameters +TERMDEPS += host-tools +TERMPROG ?= sudo sh $(CURDIR)/dist/tools/vaina/start_network.sh +TERMFLAGS ?= $(FLAGS_EXTRAS) $(IPV6_PREFIX) $(PORT) $(SLIP_BAUDRATE) + include $(RIOTBASE)/Makefile.include diff --git a/RIOT b/RIOT index f449afb..4259086 160000 --- a/RIOT +++ b/RIOT @@ -1 +1 @@ -Subproject commit f449afbc817223b7b5bacbb8e8abca2efced6905 +Subproject commit 4259086ae64417e8df9b404aac95546ab2cd03a1 diff --git a/dist/tools/vaina/Cargo.lock b/dist/tools/vaina/Cargo.lock new file mode 100644 index 0000000..3b20f02 --- /dev/null +++ b/dist/tools/vaina/Cargo.lock @@ -0,0 +1,226 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +dependencies = [ + "winapi", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi", +] + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "bytes" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1" + +[[package]] +name = "cc" +version = "1.0.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d87b23d6a92cd03af510a5ade527033f6aa6fa92161e2d5863a907d4c5e31d" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "clap" +version = "2.33.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", + "yaml-rust", +] + +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + +[[package]] +name = "hermit-abi" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61565ff7aaace3525556587bd2dc31d4a07071957be715e63ce7b1eccf51a8f4" +dependencies = [ + "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "libc" +version = "0.2.69" +source = "git+https://github.com/rust-lang/libc/#87de9103f5b48d3f8717f14646bac1dfe9495233" + +[[package]] +name = "libc" +version = "0.2.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005" + +[[package]] +name = "nix" +version = "0.17.0" +source = "git+https://github.com/nix-rust/nix#095b5be1532dabbf97208158c8a9edb15dbb7e21" +dependencies = [ + "bitflags", + "cc", + "cfg-if", + "libc 0.2.69 (git+https://github.com/rust-lang/libc/)", + "void", +] + +[[package]] +name = "proc-macro2" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8872cf6f48eee44265156c111456a700ab3483686b3f96df4cf5481c89157319" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c1f4b0efa5fc5e8ceb705136bfee52cfdb6a4e3509f770b478cd6ed434232a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "snafu" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a46f872f9a2ee1ad10a6d18003ab012a235c8f5bb90fa986202231a62e570575" +dependencies = [ + "doc-comment", + "snafu-derive", +] + +[[package]] +name = "snafu-derive" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d107ea16557fc819e8f9165ce4f5586b40449ba4a30a25814f71b442c7ddbe9e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "syn" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8e5aa70697bb26ee62214ae3288465ecec0000f05182f039b477001f08f5ae7" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "unicode-width" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" + +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" + +[[package]] +name = "vaina" +version = "0.1.0" +dependencies = [ + "bytes", + "clap", + "nix", + "snafu", +] + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "winapi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "yaml-rust" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e66366e18dc58b46801afbf2ca7661a9f59cc8c5962c29892b6039b4f86fa992" diff --git a/dist/tools/vaina/Cargo.toml b/dist/tools/vaina/Cargo.toml new file mode 100644 index 0000000..0b4da0c --- /dev/null +++ b/dist/tools/vaina/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "vaina" +version = "0.1.0" +authors = ["Locha Inc"] +edition = "2018" + +[dependencies] +clap = { version = "2", features = ["color", "yaml"] } +bytes = "0.5" +snafu = "0.6" +nix = "0.17" + +[patch.crates-io] +nix = { git = "https://github.com/nix-rust/nix" } diff --git a/dist/tools/vaina/autoconfigure.sh b/dist/tools/vaina/autoconfigure.sh new file mode 100755 index 0000000..71ed110 --- /dev/null +++ b/dist/tools/vaina/autoconfigure.sh @@ -0,0 +1,45 @@ +#!/bin/sh + +TUN=sl0 +PREFIX=128 +TUN_GLB="2001::200:0:cafe" # Dirección global de prueba +SCRIPT=$(readlink -f "$0") +BASEDIR=$(dirname "$SCRIPT") + +SUDO=${SUDO:-sudo} + +unsupported_platform() { + echo "unsupported platform" >&2 + echo "(currently supported \`uname -s\` 'Darvin' and 'Linux')" >&2 +} + +case "$(uname -s)" in + Darvin) + PLATFORM="OSX";; + Linux) + PLATFORM="Linux";; + *) + unsupported_platform + exit 1 + ;; +esac + +add_addresses() { + case "${PLATFORM}" in + Linux) + cargo build --release + ${SUDO} ip address add ${TUN_GLB} dev ${TUN} + ${SUDO} target/release/vaina rcs add ${TUN} ${TUN_GLB} + ${SUDO} target/release/vaina nib add ${TUN} ${PREFIX} ${TUN_GLB} + ;; + OSX) +# TODO: add the IPV6 address to the interface!!! + ${SUDO} target/release/vaina rcs add ${TUN} ${TUN_GLB} + ${SUDO} target/release/vaina nib add ${TUN} ${PREFIX} ${TUN_GLB} + unsupported_platform + ;; + esac + return 0 +} + +add_addresses diff --git a/dist/tools/vaina/src/cli.yml b/dist/tools/vaina/src/cli.yml new file mode 100644 index 0000000..63645df --- /dev/null +++ b/dist/tools/vaina/src/cli.yml @@ -0,0 +1,63 @@ +name: vaina +version: "0.1.0" +author: "Locha Inc " +about: "VAINA client" +subcommands: + - nib: + about: "Neighbor Information Base" + subcommands: + - add: + about: "Add route to NIB" + args: + - interface: + help: "Network interface (e.g: sl0)" + required: true + takes_value: true + - prefix: + help: "IPv6 prefix in bits (e.g: 128)" + required: true + takes_value: true + - IP: + help: "IPv6 address (e.g: 2001::db8:c0ff:ee)" + required: true + takes_value: true + - sub: + about: "Delete route from NIB" + args: + - interface: + help: "Network interface (e.g: sl0)" + required: true + takes_value: true + - prefix: + help: "IPv6 prefix in bits (e.g: 128)" + required: true + takes_value: true + - IP: + help: "IPv6 address (e.g: 2001::db8:c0ff:ee)" + required: true + takes_value: true + - rcs: + about: "Router Client Set" + subcommands: + - add: + about: "Add client to RCS" + args: + - interface: + help: "Network interface (e.g: sl0)" + required: true + takes_value: true + - IP: + help: "IPv6 address of the client to add" + required: true + takes_value: true + - sub: + about: "Delete a client from the RCS" + args: + - interface: + help: "Network interface (e.g: sl0)" + required: true + takes_value: true + - IP: + help: "IPv6 address of the client to delete" + required: true + takes_value: true diff --git a/dist/tools/vaina/src/client.rs b/dist/tools/vaina/src/client.rs new file mode 100644 index 0000000..463cd72 --- /dev/null +++ b/dist/tools/vaina/src/client.rs @@ -0,0 +1,79 @@ +use std::ffi::OsString; +use std::net::{Ipv6Addr, SocketAddr, UdpSocket}; +use std::os::unix::io::FromRawFd; +use std::str::FromStr; + +use nix::sys::socket::sockopt::{BindToDevice, Ipv6AddMembership}; +use nix::sys::socket::{ + bind, setsockopt, socket, AddressFamily, InetAddr, Ipv6Addr as NixIpv6Addr, + Ipv6MembershipRequest, SockAddr, SockFlag, SockProtocol, SockType, +}; + +use snafu::ResultExt; + +use crate::msg::Message; +use crate::*; + +/// VAINA multicast address +pub const VAINA_MCAST_ADDR: &str = "ff15::42"; +pub const VAINA_PORT: u16 = 1337; + +#[derive(Debug)] +pub struct VainaClient { + seqno: u8, + pending_acks: Vec, + sock: UdpSocket, + group: SocketAddr, +} + +impl VainaClient { + /// New `VainaClient` + pub fn new(netif: &OsString) -> Result { + let group = Ipv6Addr::from_str(VAINA_MCAST_ADDR).unwrap(); + let stdaddr = SocketAddr::new(group.clone().into(), VAINA_PORT); + let sockaddr = SockAddr::new_inet(InetAddr::from_std(&stdaddr)); + let fd = socket( + AddressFamily::Inet6, + SockType::Datagram, + SockFlag::empty(), + SockProtocol::Udp, + ) + .context(VainaSocket)?; + + setsockopt(fd, BindToDevice, netif).context(VainaSocket)?; + bind(fd, &sockaddr).context(VainaSocket)?; + + let group_req = Ipv6MembershipRequest::new(NixIpv6Addr::from_std(&group)); + setsockopt(fd, Ipv6AddMembership, &group_req).context(VainaSocket)?; + + Ok(VainaClient { + seqno: 0u8, + pending_acks: Vec::new(), + sock: unsafe { UdpSocket::from_raw_fd(fd) }, + group: stdaddr, + }) + } + + /// Craft a new sequence number + pub fn craft_seqno(&mut self) -> u8 { + let seqno = self.seqno; + self.seqno += 1; + seqno + } + + /// Send a message + pub fn send_message(&mut self, msg: &Message) -> Result<(), Error> { + // Verify if well wait for an ACK! + if !msg.is_ack() { + self.pending_acks.push(msg.seqno()); + } + + let bytes = msg.serialize(); + + self.sock + .send_to(bytes.as_ref(), &self.group) + .context(FailedSend)?; + + Ok(()) + } +} diff --git a/dist/tools/vaina/src/main.rs b/dist/tools/vaina/src/main.rs new file mode 100644 index 0000000..250879b --- /dev/null +++ b/dist/tools/vaina/src/main.rs @@ -0,0 +1,37 @@ +#[macro_use] +extern crate clap; + +use clap::App; +use snafu::Snafu; + +mod client; +mod msg; +mod nib; +mod rcs; + +#[derive(Debug, Snafu)] +pub enum Error { + #[snafu(display("Could not create socket for VAINA: {}", source))] + VainaSocket { source: nix::Error }, + #[snafu(display("Could not send data to VAINA: {}", source))] + FailedSend { source: std::io::Error }, +} + +fn main() { + let yaml = load_yaml!("cli.yml"); + let matches = App::from_yaml(yaml).get_matches(); + + let result = match matches.subcommand() { + ("rcs", Some(rcs_matches)) => rcs::handle_matches(rcs_matches), + ("nib", Some(nib_matches)) => nib::handle_matches(nib_matches), + _ => { + println!("{}", matches.usage()); + Ok(()) + } + }; + + match result { + Ok(()) => (), + Err(e) => println!("{}", e), + } +} diff --git a/dist/tools/vaina/src/msg.rs b/dist/tools/vaina/src/msg.rs new file mode 100644 index 0000000..6b20e8e --- /dev/null +++ b/dist/tools/vaina/src/msg.rs @@ -0,0 +1,113 @@ +use bytes::{BufMut, Bytes, BytesMut}; +use std::net::Ipv6Addr; + +pub const VAINA_MSG_ACK: u8 = 0; +pub const VAINA_MSG_NACK: u8 = 1; +pub const VAINA_MSG_RCS_ADD: u8 = 2; +pub const VAINA_MSG_RCS_DEL: u8 = 3; +pub const VAINA_MSG_NIB_ADD: u8 = 4; +pub const VAINA_MSG_NIB_DEL: u8 = 5; + +/// VAINA message +pub enum Message { + /// ACK + Ack { + /// Sequence number + seqno: u8, + }, + /// NACK + Nack { + /// Sequence number + seqno: u8, + }, + /// Add entry to RCS + RcsAdd { + /// Sequence number + seqno: u8, + /// Entry IPv6 address + ip: Ipv6Addr, + }, + /// Remove entry from RCS + RcsDel { + /// Sequence number + seqno: u8, + /// Entry IPv6 address + ip: Ipv6Addr, + }, + /// Add entry to NIB + NibAdd { + seqno: u8, + /// IPv6 address prefix + prefix: u8, + /// Entry IPv6 address + ip: Ipv6Addr, + }, + /// Delete entry from NIB + NibDel { + seqno: u8, + /// IPv6 address prefix + prefix: u8, + /// Entry IPv6 address + ip: Ipv6Addr, + }, +} + +impl Message { + pub fn seqno(&self) -> u8 { + match *self { + Message::Ack { seqno, .. } => seqno, + Message::Nack { seqno, .. } => seqno, + Message::RcsAdd { seqno, .. } => seqno, + Message::RcsDel { seqno, .. } => seqno, + Message::NibAdd { seqno, .. } => seqno, + Message::NibDel { seqno, .. } => seqno, + } + } + + pub fn is_ack(&self) -> bool { + match *self { + Message::Ack { .. } | Message::Nack { .. } => true, + _ => false, + } + } + + pub fn serialize(&self) -> Bytes { + let mut buf = BytesMut::with_capacity(25); + + match *self { + Message::Ack { .. } | Message::Nack { .. } => {} + Message::RcsAdd { seqno, ref ip } => { + buf.put_u8(VAINA_MSG_RCS_ADD); + buf.put_u8(seqno); + buf.put_slice(&ip.octets()); + } + Message::RcsDel { seqno, ref ip } => { + buf.put_u8(VAINA_MSG_RCS_DEL); + buf.put_u8(seqno); + buf.put_slice(&ip.octets()); + } + Message::NibAdd { + seqno, + prefix, + ref ip, + } => { + buf.put_u8(VAINA_MSG_NIB_ADD); + buf.put_u8(seqno); + buf.put_u8(prefix); + buf.put_slice(&ip.octets()); + } + Message::NibDel { + seqno, + prefix, + ref ip, + } => { + buf.put_u8(VAINA_MSG_NIB_DEL); + buf.put_u8(seqno); + buf.put_u8(prefix); + buf.put_slice(&ip.octets()); + } + } + + buf.freeze() + } +} diff --git a/dist/tools/vaina/src/nib.rs b/dist/tools/vaina/src/nib.rs new file mode 100644 index 0000000..a10a960 --- /dev/null +++ b/dist/tools/vaina/src/nib.rs @@ -0,0 +1,55 @@ +use std::ffi::OsString; +use std::net::Ipv6Addr; + +use clap::{value_t, ArgMatches}; + +use crate::client::VainaClient; +use crate::msg::Message; +use crate::Error; + +/// Router Client Set sub command +pub fn handle_matches(matches: &ArgMatches) -> Result<(), Error> { + match matches.subcommand() { + ("add", Some(add_matches)) => add(add_matches)?, + ("del", Some(del_matches)) => del(del_matches)?, + _ => println!("{}", matches.usage()), + }; + + Ok(()) +} + +fn add(matches: &ArgMatches) -> Result<(), Error> { + let prefix = value_t!(matches, "prefix", u8).unwrap_or_else(|e| e.exit()); + let ip = value_t!(matches, "IP", Ipv6Addr).unwrap_or_else(|e| e.exit()); + let interface = value_t!(matches, "interface", String).unwrap_or_else(|e| e.exit()); + let interface = OsString::from(interface); + + let mut client = VainaClient::new(&interface)?; + + let msg = Message::NibAdd { + seqno: client.craft_seqno(), + prefix, + ip, + }; + client.send_message(&msg)?; + + Ok(()) +} + +fn del(matches: &ArgMatches) -> Result<(), Error> { + let prefix = value_t!(matches, "prefix", u8).unwrap_or_else(|e| e.exit()); + let ip = value_t!(matches, "IP", Ipv6Addr).unwrap_or_else(|e| e.exit()); + let interface = value_t!(matches, "interface", String).unwrap_or_else(|e| e.exit()); + let interface = OsString::from(interface); + + let mut client = VainaClient::new(&interface)?; + + let msg = Message::NibDel { + seqno: client.craft_seqno(), + prefix, + ip, + }; + client.send_message(&msg)?; + + Ok(()) +} diff --git a/dist/tools/vaina/src/rcs.rs b/dist/tools/vaina/src/rcs.rs new file mode 100644 index 0000000..064d63c --- /dev/null +++ b/dist/tools/vaina/src/rcs.rs @@ -0,0 +1,51 @@ +use std::ffi::OsString; +use std::net::Ipv6Addr; + +use clap::{value_t, ArgMatches}; + +use crate::client::VainaClient; +use crate::msg::Message; +use crate::Error; + +/// Router Client Set sub command +pub fn handle_matches(matches: &ArgMatches) -> Result<(), Error> { + match matches.subcommand() { + ("add", Some(add_matches)) => add(add_matches)?, + ("del", Some(del_matches)) => del(del_matches)?, + _ => println!("{}", matches.usage()), + }; + + Ok(()) +} + +fn add(matches: &ArgMatches) -> Result<(), Error> { + let ip = value_t!(matches, "IP", Ipv6Addr).unwrap_or_else(|e| e.exit()); + let interface = value_t!(matches, "interface", String).unwrap_or_else(|e| e.exit()); + let interface = OsString::from(interface); + + let mut client = VainaClient::new(&interface)?; + + let msg = Message::RcsAdd { + seqno: client.craft_seqno(), + ip, + }; + client.send_message(&msg)?; + + Ok(()) +} + +fn del(matches: &ArgMatches) -> Result<(), Error> { + let ip = value_t!(matches, "IP", Ipv6Addr).unwrap_or_else(|e| e.exit()); + let interface = value_t!(matches, "interface", String).unwrap_or_else(|e| e.exit()); + let interface = OsString::from(interface); + + let mut client = VainaClient::new(&interface)?; + + let msg = Message::RcsDel { + seqno: client.craft_seqno(), + ip, + }; + client.send_message(&msg)?; + + Ok(()) +} diff --git a/dist/tools/vaina/start_network.sh b/dist/tools/vaina/start_network.sh new file mode 100755 index 0000000..d52daa9 --- /dev/null +++ b/dist/tools/vaina/start_network.sh @@ -0,0 +1,108 @@ +#!/bin/sh + +SLIPTTY_DIR="$(cd "$(dirname "$0")" && pwd -P)/../../../RIOT/dist/tools/sliptty" +TUN=sl0 +CREATED_IFACE=0 +START_SLIP=1 + +SUDO=${SUDO:-sudo} + +unsupported_platform() { + echo "unsupported platform" >&2 + echo "(currently supported \`uname -s\` 'Darvin' and 'Linux')" >&2 +} + +case "$(uname -s)" in + Darvin) + PLATFORM="OSX";; + Linux) + PLATFORM="Linux";; + *) + unsupported_platform + exit 1 + ;; +esac + +create_tun() { + case "${PLATFORM}" in + Linux) + if ! ip link show ${TUN} 2>&1 > /dev/null; then + ${SUDO} ip tuntap add ${TUN} mode tun user ${USER} || exit 1 + CREATED_IFACE=1 + fi + ${SUDO} sysctl -w net.ipv6.conf.all.forwarding=1 + echo "Activated forwarding for all interfaces." >&2 + echo "Deactivate with" >&2 + echo " ${SUDO} sysctl -w net.ipv6.conf.all.forwarding=0" >&2 + echo "when not desired without this script" >&2 + ${SUDO} sysctl -w net.ipv6.conf.${TUN}.accept_ra=2 + ${SUDO} ip link set ${TUN} up || exit 1 + ${SUDO} ip address add fe80::1/64 dev ${TUN} + ${SUDO} ip neigh add fe80::2 dev ${TUN} + ${SUDO} ip route add ${PREFIX} via fe80::2 dev ${TUN} + ;; + OSX) + # TUN interface in OSX needs to be called tunX + TUN="tun$(echo "${TUN}" | grep -o '[0-9]\+$')" + ${SUDO} chown ${USER} /dev/${TUN} || exit 1 + ${SUDO} sysctl -w net.inet6.ip6.forwarding=1 + echo "Activated forwarding for all interfaces." >&2 + echo "Deactivate with" >&2 + echo " ${SUDO} sysctl -w net.inet6.ip6.forwarding=0" >&2 + echo "when not desired without this script" >&2 + echo "start RIOT instance for ${TUN} now and hit enter" + read _ + ${SUDO} ifconfig ${TUN} up || exit 1 + ${SUDO} ifconfig ${TUN} inet6 fe80::1 prefixlen 64 + ${SUDO} route -n add -interface ${TUN} -inet6 -prefixlen 64 \ + ${PREFIX} fe80::2 || exit 1 + ;; + esac + return 0 +} + +remove_tun() { + case "${PLATFORM}" in + Linux) + ${SUDO} ip tuntap del ${TUN} mode tun + ;; + OSX) + ${SUDO} route delete -inet6 ${PREFIX} -prefixlen 64 fe80::2 + ;; + esac +} + +cleanup() { + if [ 1 -eq "${CREATED_IFACE}" ]; then + remove_tun + fi + trap "" INT QUIT TERM EXIT +} + +usage() { + echo "usage: $1 [-I ] [-d] [-e] [-g /] serial [baudrate]" + echo "usage: $1 [-I ] [-d] [-e] [-g /] tcp:host [port]" +} + +trap "cleanup" INT QUIT TERM EXIT + +while getopts dehI: opt; do + case ${opt} in + I) TUN=${OPTARG}; shift 2;; + g) TUN_GLB=${OPTARG}; shift 2;; + h) usage $0; exit 0;; + esac +done + +PREFIX="$1" +if [ -z "$PREFIX" ]; then + usage $0 + exit 1 +fi +shift + +create_tun + +START_SLIP=0 + +[ ${START_SLIP} -eq 0 ] && "${SLIPTTY_DIR}"/sliptty -I "${TUN}" "$@" diff --git a/main.c b/main.c index d7012fc..b0fe3f6 100644 --- a/main.c +++ b/main.c @@ -17,21 +17,34 @@ #include "net/aodvv2/aodvv2.h" #include "net/manet/manet.h" +#if IS_USED(MODULE_VAINA) +#include "net/vaina.h" +#endif + #if IS_USED(MODULE_SHELL_EXTENDED) #include "shell_extended.h" #endif +#ifndef CONFIG_SLIP_LOCAL_ADDR +/** + * @brief SLIP link local address + */ +#define CONFIG_SLIP_LOCAL_ADDR "fe80::dead:beef:cafe:babe" +#endif + /** - * @brief Find a IEEE 802.15.4 networ interface. + * @brief Find a network interface. * - * @return The gnrc_netif_t network interface. - * @retval NULL if no interface was found. + * @param[in] nettype The network type of the interface to find. + * + * @return The gnrc_netif_t network interface. + * @retval NULL if no interface was found. */ -static gnrc_netif_t *_find_ieee802154_netif(void); +static gnrc_netif_t *_find_netif(uint16_t nettype); int main(void) { - gnrc_netif_t *ieee802154_netif = _find_ieee802154_netif(); + gnrc_netif_t *ieee802154_netif = _find_netif(NETDEV_TYPE_IEEE802154); /* Join LL-MANET-Routers multicast group */ if (manet_netif_ipv6_group_join(ieee802154_netif) < 0) { @@ -57,6 +70,28 @@ int main(void) printf("Couldn't initialize RFC5444\n"); } +#if IS_USED(MODULE_VAINA) + gnrc_netif_t *slipdev_netif = _find_netif(NETDEV_TYPE_SLIP); + printf("found SLIP netif %d\n", slipdev_netif->pid); + if (slipdev_netif == NULL) { + printf("VAINA needs a wired interface (SLIP) to work!\n"); + } + else { + ipv6_addr_t addr; + if (ipv6_addr_from_str(&addr, CONFIG_SLIP_LOCAL_ADDR) == NULL) { + printf("Malformed SLIP local address, please verify it!\n"); + } + + if (gnrc_netif_ipv6_addr_add(slipdev_netif, &addr, 128, 0) != sizeof(ipv6_addr_t)) { + printf("Couldn't setup SLIP local address\n"); + } + + if (vaina_init(slipdev_netif) < 0) { + printf("Couldn't initialize VAINA\n"); + } + } +#endif + puts("Welcome to Turpial CC1312 Radio!"); char line_buf[SHELL_DEFAULT_BUFSIZE]; @@ -71,7 +106,7 @@ int main(void) return 0; } -static gnrc_netif_t *_find_ieee802154_netif(void) +static gnrc_netif_t *_find_netif(uint16_t nettype) { static uint16_t device_type = 0; @@ -82,16 +117,13 @@ static gnrc_netif_t *_find_ieee802154_netif(void) .data_len = sizeof(uint16_t), }; - /* Iterate over network interfaces and find one that's IEEE 802.15.4, for - * the CC1312 there is only one interface, but we need to be sure it was - * initialized. Also well be using other interface; probably SLIP over UART - * so we need to be sure it's IEEE 802.15.4 */ + /* Iterate over network interfaces and find one that matches */ gnrc_netif_t *netif = NULL; for (netif = gnrc_netif_iter(netif); netif != NULL; netif = gnrc_netif_iter(netif)) { if (gnrc_netif_get_from_netdev(netif, &opt) == sizeof(uint16_t)) { - if (device_type == NETDEV_TYPE_IEEE802154) { + if (device_type == nettype) { return netif; } } diff --git a/sys/Makefile.dep b/sys/Makefile.dep index cba40b4..4f5cda3 100644 --- a/sys/Makefile.dep +++ b/sys/Makefile.dep @@ -8,7 +8,11 @@ ifneq (,$(filter oonf_rfc5444,$(USEMODULE))) USEMODULE += oonf_common endif +ifneq (,$(filter vaina,$(USEMODULE))) + USEMODULE += gnrc_sock + USEMODULE += gnrc_sock_udp +endif + ifneq (,$(filter manet,$(USEMODULE))) USEMODULE += radio_firmware_net endif - diff --git a/sys/include/net/vaina.h b/sys/include/net/vaina.h new file mode 100644 index 0000000..fa2c3e1 --- /dev/null +++ b/sys/include/net/vaina.h @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2020 Locha Inc + * + * 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. + */ + +/** + * @ingroup net_vaina + * @{ + * + * @file + * @brief VAINA - Versatile Address Interface | Network Administration + * + * @author Jean Pierre Dudey + * @} + */ + +#ifndef NET_VAINA_H +#define NET_VAINA_H + +#include "net/gnrc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef CONFIG_VAINA_PORT +/** + * @brief VAINA UDP port + */ +#define CONFIG_VAINA_PORT (1337) +#endif + +#ifndef CONFIG_VAINA_MCAST_ADDR +/** + * @brief VAINA multicast address + */ +#define CONFIG_VAINA_MCAST_ADDR "ff15::42" +#endif + +/** + * @enum VAINA message types + */ +enum { + VAINA_MSG_ACK = 0, /**< Message acknowledged */ + VAINA_MSG_NACK = 1, /**< Message not acknowledged */ +#if IS_USED(MODULE_AODVV2) + VAINA_MSG_RCS_ADD = 2, /**< Add entry to Router Client Set */ + VAINA_MSG_RCS_DEL = 3, /**< Delete entry from Router Client Set */ +#endif + VAINA_MSG_NIB_ADD = 4, /**< Add entry to NIB */ + VAINA_MSG_NIB_DEL = 5, /**< Delete entry from NIB */ +}; + +/** + * @brief Router Client Set add message + */ +typedef struct { + ipv6_addr_t ip; /**< IP address to add */ +} vaina_msg_rcs_add_t; + +/** + * @brief Router Client Set delete message + */ +typedef struct { + ipv6_addr_t ip; /**< IP address to delete */ +} vaina_msg_rcs_del_t; + +/** + * @brief NIB add message + */ +typedef struct { + uint8_t prefix; /**< IP address prefix */ + ipv6_addr_t ip; /**< IP address to add */ +} vaina_msg_nib_add_t; + +/** + * @brief NIB del message + */ +typedef struct { + uint8_t prefix; /**< IP address prefix */ + ipv6_addr_t ip; /**< IP address to delete */ +} vaina_msg_nib_del_t; + +/** + * @brief VAINA message + */ +typedef struct { + uint8_t msg; /**< Message type */ + uint8_t seqno; /**< Sequence number */ + union { + vaina_msg_rcs_add_t rcs_add; /**< VAINA_MSG_RCS_ADD */ + vaina_msg_rcs_del_t rcs_del; /**< VAINA_MSG_RCS_DEL */ + vaina_msg_nib_add_t nib_add; /**< VAINA_MSG_NIB_ADD */ + vaina_msg_nib_del_t nib_del; /**< VAINA_MSG_NIB_DEL */ + } payload; /** Payload of the message */ +} vaina_msg_t; + +/** + * @brief Initialize VAINA server. + */ +int vaina_init(gnrc_netif_t *netif); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* NET_VAINA_H */ diff --git a/sys/net/Kconfig b/sys/net/Kconfig index 3d36888..ce6e2c6 100644 --- a/sys/net/Kconfig +++ b/sys/net/Kconfig @@ -3,5 +3,6 @@ menu "Network" rsource "aodvv2/Kconfig" +rsource "vaina/Kconfig" endmenu diff --git a/sys/net/Makefile b/sys/net/Makefile index fb856c1..385e2fb 100644 --- a/sys/net/Makefile +++ b/sys/net/Makefile @@ -2,5 +2,8 @@ MODULE = radio_firmware_net DIRS += aodvv2 DIRS += manet +ifneq (,$(filter vaina,$(USEMODULE))) + DIRS += vaina +endif include $(RIOTBASE)/Makefile.base diff --git a/sys/net/vaina/Kconfig b/sys/net/vaina/Kconfig new file mode 100644 index 0000000..ccf6999 --- /dev/null +++ b/sys/net/vaina/Kconfig @@ -0,0 +1,24 @@ +menuconfig KCONFIG_MODULE_VAINA + bool "VAINA - Versatile Address Interface | Network Administration" + depends on MODULE_VAINA + help + Configures VAINA - Versatile Address Interface | Network Administration + using Kconfig. + +if KCONFIG_MODULE_VAINA + +config VAINA_PORT + int "Port used for the VAINA UDP server" + default 1337 + range 0 65536 + help + Port used for the VAINA UDP server + +config VAINA_MCAST_ADDR + string "VAINA multicast address" + default "ff15::42" + help + Multicast address used for the VAINA server. This is where VAINA will + listen to messages (that are sent to the correct configured port). + +endif diff --git a/sys/net/vaina/Makefile b/sys/net/vaina/Makefile new file mode 100644 index 0000000..5064b14 --- /dev/null +++ b/sys/net/vaina/Makefile @@ -0,0 +1,3 @@ +MODULE = vaina + +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/vaina/vaina.c b/sys/net/vaina/vaina.c new file mode 100644 index 0000000..08f9ff3 --- /dev/null +++ b/sys/net/vaina/vaina.c @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2020 Locha Inc + * + * 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. + */ + +/** + * @ingroup net_vaina + * @{ + * + * @file + * @brief VAINA - Versatile Address Interface | Network Administration + * + * @author Jean Pierre Dudey + * @} + */ + +#include "net/vaina.h" + +#include "net/sock/udp.h" +#include "net/gnrc/ipv6/nib/ft.h" + +#if IS_USED(MODULE_AODVV2) +#include "net/aodvv2/client.h" +#endif + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#if ENABLE_DEBUG == 1 +static char _stack[THREAD_STACKSIZE_DEFAULT + THREAD_EXTRA_STACKSIZE_PRINTF]; +#else +static char _stack[THREAD_STACKSIZE_DEFAULT]; +#endif + +/** + * @brief UDP socket + */ +static sock_udp_t _sock; + +/** + * @brief GNRC netif + */ +static gnrc_netif_t *_netif; + +/** + * @brief UDP local endpoint + */ +static sock_udp_ep_t _local; + +static int _parse_msg(vaina_msg_t *vaina, uint8_t *buf, size_t len) +{ + memset(vaina, 0, sizeof(vaina_msg_t)); + + if (len < 2) { + DEBUG_PUTS("vaina: invalid message size!"); + return -EINVAL; + } + + uint8_t type = buf[0]; + uint8_t seqno = buf[1]; + + switch (type) { +#if IS_USED(MODULE_AODVV2) + case VAINA_MSG_RCS_ADD: + case VAINA_MSG_RCS_DEL: + if (len < (2 + sizeof(ipv6_addr_t))) { + return -EINVAL; + } + vaina->msg = type; + vaina->seqno = seqno; + if (vaina->msg == VAINA_MSG_RCS_ADD) { + memcpy(&vaina->payload.rcs_add.ip, &buf[2], sizeof(ipv6_addr_t)); + } + else { + memcpy(&vaina->payload.rcs_del.ip, &buf[2], sizeof(ipv6_addr_t)); + } + break; +#endif + + case VAINA_MSG_NIB_ADD: + case VAINA_MSG_NIB_DEL: + if (len < (2 + 1 + sizeof(ipv6_addr_t))) { + return -EINVAL; + } + vaina->msg = type; + vaina->seqno = seqno; + if (vaina->msg == VAINA_MSG_RCS_ADD) { + vaina->payload.nib_add.prefix = buf[2]; + memcpy(&vaina->payload.nib_add.ip, &buf[3], sizeof(ipv6_addr_t)); + } + else { + vaina->payload.nib_del.prefix = buf[2]; + memcpy(&vaina->payload.nib_del.ip, &buf[3], sizeof(ipv6_addr_t)); + } + break; + + default: + DEBUG_PUTS("vaina: invalid message type"); + return -EINVAL; + } + + return 0; +} + +static int _process_msg(vaina_msg_t *msg) +{ + switch (msg->msg) { +#if IS_USED(MODULE_AODVV2) + case VAINA_MSG_RCS_ADD: + DEBUG_PUTS("vaina: adding new client"); + if (aodvv2_client_add(&msg->payload.rcs_add.ip, 128, 1) == NULL) { + DEBUG_PUTS("vaina: client set is full"); + return -ENOSPC; + } + break; + + case VAINA_MSG_RCS_DEL: + aodvv2_client_delete(&msg->payload.rcs_del.ip); + break; +#endif + + case VAINA_MSG_NIB_ADD: + DEBUG_PUTS("vaina: adding NIB entry"); + if (gnrc_ipv6_nib_ft_add(&msg->payload.nib_add.ip, + msg->payload.nib_add.prefix, + NULL, _netif->pid, 0) < 0) { + return -ENOMEM; + } + break; + + case VAINA_MSG_NIB_DEL: + gnrc_ipv6_nib_ft_del(&msg->payload.nib_del.ip, + msg->payload.nib_del.prefix); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int _send_ack(vaina_msg_t *msg, sock_udp_ep_t *remote, bool good_ack) +{ + DEBUG("vaina: sending %s\n", good_ack ? "ack" : "nack"); + uint8_t buf[2]; + if (good_ack) { + buf[0] = VAINA_MSG_ACK; + } + else { + buf[0] = VAINA_MSG_NACK; + } + buf[1] = msg->seqno; + + return sock_udp_send(&_sock, buf, sizeof(buf), remote); +} + +static void *_vaina_thread(void *arg) +{ + (void) arg; + uint8_t buf[UINT8_MAX]; + sock_udp_ep_t remote; + vaina_msg_t msg; + + while (true) { + int received = sock_udp_recv(&_sock, buf, sizeof(buf), SOCK_NO_TIMEOUT, + &remote); + + DEBUG("vaina: received new packet\n"); + + /* TODO: why remote.netif is equal to 0 when sending a receiving a + * packet from SLIP + */ + if (remote.netif != _netif->pid && remote.netif != 0) { + DEBUG("vaina: not from our netif: %d\n", remote.netif); + continue; + } + + if (received < 0) { + DEBUG_PUTS("vaina: couldn't receive packet"); + continue; + } + + if (received == 0) { + DEBUG_PUTS("vaina: packet doesn't have a payload, dropping"); + continue; + } + + if (_parse_msg(&msg, buf, received) < 0) { + DEBUG_PUTS("vaina: couldn't parse received message."); + continue; + } + + bool good_ack = true; + if (_process_msg(&msg) < 0) { + DEBUG_PUTS("vaina: couldn't process message."); + good_ack = false; + } + + if (_send_ack(&msg, &remote, good_ack) < 0) { + DEBUG_PUTS("vaina: couldn't send the ACK!"); + } + } + + /* Never reached */ + return NULL; +} + +int vaina_init(gnrc_netif_t *netif) +{ + assert(netif != NULL); + + ipv6_addr_t group; + if (ipv6_addr_from_str(&group, CONFIG_VAINA_MCAST_ADDR) == NULL) { + DEBUG_PUTS("vaina: invalid IPv6 group address"); + return -EINVAL; + } + + if (gnrc_netif_ipv6_group_join(netif, &group) < 0) { + DEBUG_PUTS("vaina: coudln't joint VAINA IPv6 group"); + return -1; + } + + _local.family = AF_INET6; + memcpy(&_local.addr, &group, sizeof(ipv6_addr_t)); + _local.netif = netif->pid; + _local.port = CONFIG_VAINA_PORT; + if (sock_udp_create(&_sock, &_local, NULL, 0) < 0) { + DEBUG_PUTS("vaina: couldn't create UDP socket"); + return -1; + } + + _netif = netif; + + return thread_create(_stack, sizeof(_stack), THREAD_PRIORITY_MAIN + 2, + THREAD_CREATE_STACKTEST, _vaina_thread, NULL, "vaina"); +}