Compare commits

...

No commits in common. "main" and "wip/rust" have entirely different histories.

32 changed files with 513 additions and 1071 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
target
*.rs.bk

21
Cargo.lock generated Normal file
View file

@ -0,0 +1,21 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "libc"
version = "0.2.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49"
[[package]]
name = "neco"
version = "0.1.0"
dependencies = [
"safe_libc",
]
[[package]]
name = "safe_libc"
version = "0.1.0"
dependencies = [
"libc",
]

16
Cargo.toml Normal file
View file

@ -0,0 +1,16 @@
[package]
name = "neco"
version = "0.1.0"
authors = ["Matthias Schiffer <mschiffer@universe-factory.net>"]
edition = "2018"
[dependencies]
safe_libc = { path = "safe_libc" }
[profile.dev]
lto = true
panic = "abort"
[profile.release]
lto = true
panic = "abort"

22
LICENSE
View file

@ -1,22 +0,0 @@
Copyright (c) 2012-2019, Matthias Schiffer <mschiffer@universe-factory.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -1,9 +0,0 @@
project('neco', 'c', default_options : ['c_std=gnu11'])
cc = meson.get_compiler('c')
libubox_dep = cc.find_library('ubox')
libjson_c_dep = dependency('json-c', method : 'pkg-config')
libmnl_dep = dependency('libmnl', method : 'pkg-config')
subdir('src')

1
rustfmt.toml Normal file
View file

@ -0,0 +1 @@
hard_tabs = true

10
safe_libc/Cargo.toml Normal file
View file

@ -0,0 +1,10 @@
[package]
name = "safe_libc"
version = "0.1.0"
authors = ["Matthias Schiffer <mschiffer@universe-factory.net>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
libc = { version = "0.2.71", default-features = false }

78
safe_libc/src/boxed.rs Normal file
View file

@ -0,0 +1,78 @@
use core::{marker, mem, ptr};
use core::ops::{Deref, DerefMut};
pub trait Dealloc<T: ?Sized> {
unsafe fn dealloc(p: *mut T);
}
#[repr(transparent)]
pub struct BoxLike<T: ?Sized, D: Dealloc<T>> {
inner: ptr::NonNull<T>,
_phantom: marker::PhantomData<T>,
_dropper: marker::PhantomData<D>,
}
impl<T: ?Sized, D: Dealloc<T>> BoxLike<T, D> {
#[inline]
pub unsafe fn from_raw_unchecked(p: *mut T) -> Self {
BoxLike {
inner: ptr::NonNull::new_unchecked(p),
_phantom: marker::PhantomData,
_dropper: marker::PhantomData,
}
}
#[inline]
pub unsafe fn from_raw(p: *mut T) -> Option<Self> {
if p.is_null() {
None
} else {
Some(Self::from_raw_unchecked(p))
}
}
#[inline]
pub fn into_raw(self) -> *mut T {
let p = self.inner.as_ptr();
mem::forget(self);
p
}
}
impl<T: ?Sized, D: Dealloc<T>> Drop for BoxLike<T, D> {
#[inline]
fn drop(&mut self) {
let p = self.inner.as_ptr();
unsafe {
ptr::drop_in_place(p);
D::dealloc(p);
}
}
}
impl<T: ?Sized, D: Dealloc<T>> Deref for BoxLike<T, D> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
unsafe { self.inner.as_ref() }
}
}
impl<T: ?Sized, D: Dealloc<T>> DerefMut for BoxLike<T, D> {
#[inline]
fn deref_mut(&mut self) -> &mut T {
unsafe { self.inner.as_mut() }
}
}
pub struct DeallocFree;
impl<T: ?Sized> Dealloc<T> for DeallocFree {
#[inline]
unsafe fn dealloc(p: *mut T) {
libc::free(p as _);
}
}
pub type CBox<T> = BoxLike<T, DeallocFree>;

34
safe_libc/src/errno.rs Normal file
View file

@ -0,0 +1,34 @@
use crate::string;
use core::fmt;
#[derive(Clone, Copy, Debug)]
#[repr(transparent)]
pub struct Errno(pub libc::c_int);
extern "C" {
#[ffi_const]
pub fn __errno_location() -> *mut libc::c_int;
}
#[inline]
pub fn errno() -> Errno {
unsafe { Errno(*__errno_location()) }
}
impl fmt::Display for Errno {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut buf = [0u8; 1024];
let cstr = unsafe {
if libc::strerror_r(self.0, buf.as_mut_ptr().cast(), buf.len()) != 0 {
return Err(fmt::Error);
}
string::CStr::from_bytes_with_nul_unchecked(&buf)
};
match cstr.to_str() {
Err(_) => Err(fmt::Error),
Ok(s) => f.write_str(s),
}
}
}

21
safe_libc/src/lib.rs Normal file
View file

@ -0,0 +1,21 @@
#![no_std]
#![feature(extern_types)]
#![feature(slice_ptr_len)]
#![feature(ffi_const)]
extern crate alloc;
pub use libc::*;
mod util;
pub mod boxed;
pub mod errno;
pub mod stdio;
pub mod string;
extern "C" {
// pub static stdin: *mut libc::FILE;
pub static stdout: *mut libc::FILE;
pub static stderr: *mut libc::FILE;
}

104
safe_libc/src/stdio.rs Normal file
View file

@ -0,0 +1,104 @@
use crate::{self as libc, errno, string};
use core::fmt;
use core::ops::{Deref, DerefMut};
pub struct Error {
pub errno: errno::Errno,
}
impl From<Error> for fmt::Error {
fn from(_err: Error) -> fmt::Error {
fmt::Error
}
}
pub type Result<T> = core::result::Result<T, Error>;
#[inline]
fn check_io(ok: bool) -> Result<()> {
if ok {
Ok(())
} else {
Err(Error {
errno: errno::errno(),
})
}
}
pub struct BasicOStream(*mut libc::FILE);
unsafe impl Sync for BasicOStream {}
unsafe impl Send for BasicOStream {}
pub type Stdout = BasicOStream;
pub type Stderr = BasicOStream;
#[inline]
pub fn stdout() -> Stdout {
BasicOStream(unsafe { libc::stdout })
}
#[inline]
pub fn stderr() -> Stderr {
BasicOStream(unsafe { libc::stderr })
}
impl BasicOStream {
#[inline]
pub fn flush(&mut self) -> Result<()> {
check_io(unsafe { libc::fflush(self.0) } == 0)
}
#[inline]
pub fn write(&mut self, b: &[u8]) -> Result<()> {
check_io(unsafe { libc::fwrite(b.as_ptr().cast(), 1, b.len(), self.0) } == b.len())
}
#[inline]
pub fn puts(&mut self, s: &string::CStr) -> Result<()> {
check_io(unsafe { libc::fputs(s.as_ptr(), self.0) } != libc::EOF)
}
}
impl fmt::Write for BasicOStream {
#[inline]
fn write_str(&mut self, s: &str) -> fmt::Result {
self.write(s.as_bytes())?;
Ok(())
}
}
pub struct OStream(BasicOStream);
impl OStream {
#[inline]
pub unsafe fn from_raw(file: *mut libc::FILE) -> OStream {
OStream(BasicOStream(file))
}
}
impl Drop for OStream {
#[inline]
fn drop(&mut self) {
unsafe {
libc::fclose((self.0).0);
}
}
}
impl Deref for OStream {
type Target = BasicOStream;
#[inline]
fn deref(&self) -> &BasicOStream {
&self.0
}
}
impl DerefMut for OStream {
#[inline]
fn deref_mut(&mut self) -> &mut BasicOStream {
&mut self.0
}
}

142
safe_libc/src/string.rs Normal file
View file

@ -0,0 +1,142 @@
use crate::{self as libc, boxed::CBox, util};
use core::{mem, slice};
use core::ops::{Deref, DerefMut};
//pub struct FromBytesWithNulError {}
extern "C" {
type RawCStr;
}
#[repr(transparent)]
pub struct CStr(RawCStr);
impl CStr {
#[inline]
pub unsafe fn from_ptr<'a>(p: *const libc::c_char) -> &'a CStr {
&*(p as *const CStr)
}
#[inline]
pub unsafe fn from_mut_ptr<'a>(p: *mut libc::c_char) -> &'a mut CStr {
&mut *(p as *mut CStr)
}
#[inline]
pub unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> &CStr {
CStr::from_ptr(bytes.as_ptr().cast())
}
// TODO
//pub fn from_bytes_with_nul(bytes: &[u8]) -> Result<&CStr, FromBytesWithNulError> {
//}
#[inline]
pub fn len(&self) -> usize {
unsafe { libc::strlen(self.as_ptr()) }
}
#[inline]
pub const fn as_ptr(&self) -> *const libc::c_char {
(self as *const CStr).cast()
}
#[inline]
pub fn as_mut_ptr(&mut self) -> *mut libc::c_char {
(self as *mut CStr).cast()
}
#[inline]
pub fn to_bytes(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self.as_ptr().cast(), self.len()) }
}
#[inline]
pub fn to_mut_bytes(&mut self) -> &mut [u8] {
unsafe { slice::from_raw_parts_mut(self.as_mut_ptr().cast(), self.len()) }
}
#[inline]
pub fn to_str(&self) -> Result<&str, core::str::Utf8Error> {
core::str::from_utf8(self.to_bytes())
}
#[inline]
pub fn to_mut_str(&mut self) -> Result<&mut str, core::str::Utf8Error> {
core::str::from_utf8_mut(self.to_mut_bytes())
}
#[inline]
pub fn to_owned(self: &CStr) -> CString {
CString::from(self)
}
}
#[macro_export]
macro_rules! cstr {
($s:expr) => {
unsafe { $crate::string::CStr::from_bytes_with_nul_unchecked(concat!($s, "\0").as_bytes()) }
};
}
pub struct CString {
inner: CBox<CStr>,
}
impl CString {
#[inline]
pub unsafe fn from_raw(p: *mut libc::c_char) -> CString {
CString {
inner: CBox::from_raw_unchecked(p as *mut CStr)
}
}
#[inline]
pub fn into_raw(mut self) -> *mut libc::c_char {
let p = self.as_mut_ptr();
mem::forget(self);
p
}
}
impl Deref for CString {
type Target = CStr;
#[inline]
fn deref(&self) -> &CStr {
&*self.inner
}
}
impl DerefMut for CString {
#[inline]
fn deref_mut(&mut self) -> &mut CStr {
&mut *self.inner
}
}
impl From<&[u8]> for CString {
fn from(s: &[u8]) -> CString {
unsafe {
CString::from_raw(util::must_succeed(libc::strndup(
s.as_ptr().cast(),
s.len(),
)))
}
}
}
impl From<&str> for CString {
#[inline]
fn from(s: &str) -> CString {
CString::from(s.as_bytes())
}
}
impl From<&CStr> for CString {
#[inline]
fn from(s: &CStr) -> CString {
unsafe { CString::from_raw(util::must_succeed(libc::strdup(s.as_ptr()))) }
}
}

9
safe_libc/src/util.rs Normal file
View file

@ -0,0 +1,9 @@
use alloc::alloc;
#[inline]
pub fn must_succeed<T>(p: *mut T) -> *mut T {
if p.is_null() {
alloc::handle_alloc_error(alloc::Layout::new::<T>())
}
p
}

View file

@ -1,41 +0,0 @@
#include "config-load.h"
#include "config-process.h"
#include "device.h"
#include "util.h"
#include <libubox/avl-cmp.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
bool read_config(const char *path) {
struct json_object *config = json_object_from_file(path);
if (!config)
return false;
struct avl_tree *devices = config_process(config);
json_object_put(config);
if (!devices)
return false;
device_t *dev, *tmp;
avl_for_each_element(devices, dev, node)
dev->type->init(dev);
//avl_for_each_element(devices, dev, node)
// dev->type->release(dev);
avl_remove_all_elements(devices, dev, node, tmp)
dev->type->free(dev);
free(devices);
return true;
}

View file

@ -1,5 +0,0 @@
#pragma once
#include <stdbool.h>
bool read_config(const char *path);

View file

@ -1,58 +0,0 @@
#include "config-process.h"
#include "device.h"
#include "util.h"
#include <libubox/avl-cmp.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
typedef struct _process_ctx {
struct avl_tree *ret;
} process_ctx_t;
typedef struct _config_subtype {
struct avl_node node;
} config_subtype_t;
static device_t * config_process_device(const char *name, struct json_object *obj) {
const char *typename = NULL;
struct json_object *device = neco_json_get_value(obj, "device", json_type_object);
if (device)
typename = neco_json_get_string(device, "type");
const device_type_t *type = get_device_type(typename ?: "interface");
if (!type)
return NULL;
return type->process_config(name, obj);
}
struct avl_tree * config_process(struct json_object *config) {
if (!json_object_is_type(config, json_type_object))
return NULL;
struct json_object *devices = neco_json_get_value(config, "devices", json_type_object);
if (!devices)
return NULL;
process_ctx_t ctx;
ctx.ret = calloc(1, sizeof(*ctx.ret));
if (!ctx.ret)
return NULL;
avl_init(ctx.ret, avl_strcmp, false, NULL);
json_object_object_foreach(devices, name, obj) {
device_t *device = config_process_device(name, obj);
if (!device)
continue;
avl_insert(ctx.ret, &device->node);
}
return ctx.ret;
}

View file

@ -1,6 +0,0 @@
#pragma once
#include <json-c/json.h>
#include <libubox/avl.h>
struct avl_tree * config_process(struct json_object *config);

View file

@ -1,131 +0,0 @@
#include "device-common.h"
#include "netlink.h"
#include "util.h"
#include <string.h>
#include <net/if.h>
#include <linux/if.h>
#include <linux/if_link.h>
#include <linux/rtnetlink.h>
static device_type_t device_type_bridge;
typedef struct _device_bridge {
device_t device;
device_common_t common;
} device_bridge_t;
static void bridge_free(device_t *dev) {
device_bridge_t *iface = container_of(dev, device_bridge_t, device);
VECTOR_FREE(iface->common.addrs);
free(NODE_NAME(dev));
free(iface);
}
static device_t * bridge_process_config(const char *name, struct json_object *config) {
device_bridge_t *iface = calloc(1, sizeof(*iface));
if (!iface)
return NULL;
device_t *dev = &iface->device;
dev->type = &device_type_bridge;
NODE_NAME(dev) = strdup(name);
if (!NODE_NAME(dev)) {
free(iface);
return NULL;
}
if (!device_common_process_config(&iface->common, config))
goto err;
return dev;
err:
bridge_free(dev);
return NULL;
}
static bool bridge_nl_create(device_bridge_t *bridge) {
char buf[MNL_SOCKET_BUFFER_SIZE];
struct mnl_socket *nl = nl_socket();
struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
nlh->nlmsg_type = RTM_NEWLINK;
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE;
int seq = nlh->nlmsg_seq = nl_seq();
struct ifinfomsg *ifi = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifi));
ifi->ifi_family = AF_UNSPEC;
mnl_attr_put_str(nlh, IFLA_IFNAME, NODE_NAME(&bridge->device));
struct nlattr *linkinfo = mnl_attr_nest_start(nlh, IFLA_LINKINFO);
mnl_attr_put_str(nlh, IFLA_INFO_KIND, "bridge");
mnl_attr_nest_end(nlh, linkinfo);
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
perror("mnl_socket_sendto");
return false;
}
int ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
if (ret == -1) {
perror("mnl_socket_recvfrom");
return false;
}
ret = mnl_cb_run(buf, ret, seq, mnl_socket_get_portid(nl), NULL, NULL);
if (ret == -1) {
perror("mnl_cb_run");
return false;
}
return true;
}
static void bridge_init(device_t *dev) {
device_bridge_t *bridge = container_of(dev, device_bridge_t, device);
if (!bridge_nl_create(bridge))
return;
unsigned ifindex = if_nametoindex(NODE_NAME(dev));
if (!ifindex)
return;
device_common_init(&bridge->common, ifindex);
}
static void bridge_update(device_t *dev) {
}
static void bridge_release(device_t *dev) {
/*
device_bridge_t *bridge = container_of(dev, device_bridge_t, device);
unsigned ifindex = if_nametoindex(NODE_NAME(dev));
if (!ifindex)
return;
device_common_release(&bridge->common, ifindex);
*/
}
static device_type_t device_type_bridge = {
.process_config = bridge_process_config,
.free = bridge_free,
.init = bridge_init,
.update = bridge_update,
.release = bridge_release,
};
__attribute__((constructor))
static void bridge_constructor(void) {
register_device_type("bridge", &device_type_bridge);
}

View file

@ -1,299 +0,0 @@
#include "device-common.h"
#include "netlink.h"
#include "util.h"
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <linux/if.h>
#include <linux/if_link.h>
#include <linux/rtnetlink.h>
static unsigned long strtoul_safe(const char *str) {
char *endptr;
errno = 0;
unsigned long val = strtoul(str, &endptr, 10);
if (endptr == str || *endptr)
errno = EINVAL;
return val;
}
static bool parse_address(ipaddr_t *addr, const char *str) {
if (inet_pton(AF_INET, str, &addr->addr4)) {
addr->af = AF_INET;
return true;
}
if (inet_pton(AF_INET6, str, &addr->addr6)) {
addr->af = AF_INET6;
return true;
}
return false;
}
static bool parse_prefix(ipaddr_prefix_t *prefix, const char *str, bool allow_host) {
const char *slash = strrchr(str, '/');
if (!slash)
return false;
size_t len = slash - str;
char buf[len+1];
memcpy(buf, str, len);
buf[len] = 0;
if (!parse_address(&prefix->addr, buf))
return false;
long plen = strtoul_safe(slash + 1);
if (errno)
return false;
switch (prefix->addr.af) {
case AF_INET:
if (plen > 32)
return false;
break;
case AF_INET6:
if (plen > 128)
return false;
break;
default:
assert(false);
__builtin_unreachable();
}
prefix->plen = plen;
// TODO: Implement allow_host
return true;
}
static bool process_section_device(device_common_t *device, struct json_object *section) {
return true;
}
static bool process_section_static(device_common_t *device, struct json_object *section) {
struct json_object *addresses = neco_json_get_value(section, "addresses", json_type_array);
if (addresses) {
for (size_t i = 0; i < json_object_array_length(addresses); i++) {
struct json_object *address = json_object_array_get_idx(addresses, i);
if (!json_object_is_type(address, json_type_string)) {
fprintf(stderr, "interface: static: invalid address entry of type %s\n", json_type_to_name(json_object_get_type(address)));
continue;
}
ipaddr_prefix_t p;
if (!parse_prefix(&p, json_object_get_string(address), true)) {
fprintf(stderr, "interface: static: unable to parse Address %s\n", json_object_get_string(address));
break;
}
if (!VECTOR_ADD(device->addrs, p)) {
fprintf(stderr, "interface: static: adding address failed\n");
return false;
}
}
}
return true;
}
static bool process_section_bridge(device_common_t *device, struct json_object *section) {
const char *master = neco_json_get_string(section, "master");
if (master)
strncpy(device->master, master, sizeof(device->master)-1);
return true;
}
bool device_common_process_config(device_common_t *device, struct json_object *config) {
struct json_object *sec_device = neco_json_get_value(config, "device", json_type_object);
if (sec_device) {
if (!process_section_device(device, sec_device))
return false;
}
struct json_object *sec_static = neco_json_get_value(config, "static", json_type_object);
if (sec_static) {
if (!process_section_static(device, sec_static))
return false;
}
struct json_object *sec_bridge = neco_json_get_value(config, "bridge", json_type_object);
if (sec_bridge) {
if (!process_section_bridge(device, sec_bridge))
return false;
}
return true;
}
static bool device_common_set_link_flags(unsigned ifindex, unsigned change, unsigned flags) {
char buf[MNL_SOCKET_BUFFER_SIZE];
struct mnl_socket *nl = nl_socket();
struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
nlh->nlmsg_type = RTM_SETLINK;
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
int seq = nlh->nlmsg_seq = nl_seq();
struct ifinfomsg *ifi = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifi));
ifi->ifi_index = ifindex;
ifi->ifi_family = AF_UNSPEC;
ifi->ifi_change = change;
ifi->ifi_flags = flags;
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
perror("mnl_socket_sendto");
return false;
}
int ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
if (ret == -1) {
perror("mnl_socket_recvfrom");
return false;
}
ret = mnl_cb_run(buf, ret, seq, mnl_socket_get_portid(nl), NULL, NULL);
if (ret == -1) {
perror("mnl_cb_run");
return false;
}
return true;
}
static bool device_common_set_link_state(unsigned ifindex, bool up) {
return device_common_set_link_flags(ifindex, IFF_UP, up ? IFF_UP : 0);
}
static bool device_common_set_ipaddr(unsigned ifindex, ipaddr_prefix_t *addr, bool add) {
char buf[MNL_SOCKET_BUFFER_SIZE];
struct mnl_socket *nl = nl_socket();
struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
nlh->nlmsg_type = add ? RTM_NEWADDR : RTM_DELADDR;
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
if (add)
nlh->nlmsg_flags |= NLM_F_CREATE | NLM_F_REPLACE;
int seq = nlh->nlmsg_seq = nl_seq();
struct ifaddrmsg *ifa = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifa));
ifa->ifa_index = ifindex;
ifa->ifa_family = addr->addr.af;
ifa->ifa_prefixlen = addr->plen;
switch (addr->addr.af) {
case AF_INET:
mnl_attr_put(nlh, IFA_LOCAL, 4, &addr->addr.addr4);
break;
case AF_INET6:
mnl_attr_put(nlh, IFA_LOCAL, 16, &addr->addr.addr6);
break;
default:
errno = EINVAL;
return false;
}
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
perror("mnl_socket_sendto");
return false;
}
int ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
if (ret == -1) {
perror("mnl_socket_recvfrom");
return false;
}
ret = mnl_cb_run(buf, ret, seq, mnl_socket_get_portid(nl), NULL, NULL);
if (ret == -1) {
perror("mnl_cb_run");
return false;
}
return true;
}
static bool device_common_set_master(unsigned ifindex, unsigned master) {
char buf[MNL_SOCKET_BUFFER_SIZE];
struct mnl_socket *nl = nl_socket();
struct nlmsghdr *nlh = mnl_nlmsg_put_header(buf);
nlh->nlmsg_type = RTM_SETLINK;
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
int seq = nlh->nlmsg_seq = nl_seq();
struct ifinfomsg *ifi = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifi));
ifi->ifi_index = ifindex;
ifi->ifi_family = AF_UNSPEC;
mnl_attr_put_u32(nlh, IFLA_MASTER, master);
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
perror("mnl_socket_sendto");
return false;
}
int ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
if (ret == -1) {
perror("mnl_socket_recvfrom");
return false;
}
ret = mnl_cb_run(buf, ret, seq, mnl_socket_get_portid(nl), NULL, NULL);
if (ret == -1) {
perror("mnl_cb_run");
return false;
}
return true;
}
void device_common_init(device_common_t *device, int ifindex) {
if (*device->master) {
unsigned master = if_nametoindex(device->master);
if (!master)
return;
device_common_set_master(ifindex, master);
}
device_common_set_link_state(ifindex, true);
if (!*device->master) {
for (size_t i = 0; i < VECTOR_LEN(device->addrs); i++)
device_common_set_ipaddr(ifindex, &VECTOR_INDEX(device->addrs, i), true);
}
}
void device_common_release(device_common_t *device, int ifindex) {
if (!*device->master) {
for (size_t i = 0; i < VECTOR_LEN(device->addrs); i++)
device_common_set_ipaddr(ifindex, &VECTOR_INDEX(device->addrs, i), false);
}
device_common_set_link_state(ifindex, false);
if (*device->master)
device_common_set_master(ifindex, 0);
}

View file

@ -1,36 +0,0 @@
#pragma once
#include "device.h"
#include "vector.h"
#include <net/if.h>
#include <netinet/ether.h>
#include <netinet/in.h>
typedef struct _ipaddr {
int af;
union {
struct in_addr addr4;
struct in6_addr addr6;
};
} ipaddr_t;
typedef struct _ipprefix {
ipaddr_t addr;
uint8_t plen;
} ipaddr_prefix_t;
typedef VECTOR(ipaddr_prefix_t) ipaddr_prefix_vector_t;
typedef struct _device_common {
struct ether_addr macaddr;
uint16_t mtu;
ipaddr_prefix_vector_t addrs;
char master[IF_NAMESIZE];
} device_common_t;
bool device_common_process_config(device_common_t *device, struct json_object *config);
void device_common_init(device_common_t *device, int ifindex);
void device_common_release(device_common_t *device, int ifindex);

View file

@ -1,85 +0,0 @@
#include "device-common.h"
#include "util.h"
#include "vector.h"
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <net/if.h>
static device_type_t device_type_interface;
typedef struct _device_interface {
device_t device;
device_common_t common;
} device_interface_t;
static void interface_free(device_t *dev) {
device_interface_t *iface = container_of(dev, device_interface_t, device);
VECTOR_FREE(iface->common.addrs);
free(NODE_NAME(dev));
free(iface);
}
static device_t * interface_process_config(const char *name, struct json_object *config) {
device_interface_t *iface = calloc(1, sizeof(*iface));
if (!iface)
return NULL;
device_t *dev = &iface->device;
dev->type = &device_type_interface;
NODE_NAME(dev) = strdup(name);
if (!NODE_NAME(dev)) {
free(iface);
return NULL;
}
if (!device_common_process_config(&iface->common, config))
goto err;
return dev;
err:
interface_free(dev);
return NULL;
}
static void interface_init(device_t *dev) {
device_interface_t *iface = container_of(dev, device_interface_t, device);
unsigned ifindex = if_nametoindex(NODE_NAME(dev));
if (!ifindex)
return;
device_common_init(&iface->common, ifindex);
}
static void interface_update(device_t *dev) {
}
static void interface_release(device_t *dev) {
device_interface_t *iface = container_of(dev, device_interface_t, device);
unsigned ifindex = if_nametoindex(NODE_NAME(dev));
if (!ifindex)
return;
device_common_release(&iface->common, ifindex);
}
static device_type_t device_type_interface = {
.process_config = interface_process_config,
.free = interface_free,
.init = interface_init,
.update = interface_update,
.release = interface_release,
};
__attribute__((constructor))
static void interface_constructor(void) {
register_device_type("interface", &device_type_interface);
}

View file

@ -1,15 +0,0 @@
#include "device.h"
#include "util.h"
#include <libubox/avl-cmp.h>
AVL_TREE(device_types, avl_strcmp, false, NULL);
void register_device_type(const char *name, device_type_t *device_type) {
NODE_NAME(device_type) = (char *)name;
avl_insert(&device_types, &device_type->node);
}
const device_type_t * get_device_type(const char *name) {
return avl_find_element(&device_types, name, (device_type_t *)NULL, node);
}

View file

@ -1,26 +0,0 @@
#pragma once
#include <json-c/json.h>
#include <libubox/avl.h>
extern struct avl_tree device_types;
typedef struct _device_type device_type_t;
typedef struct _device {
struct avl_node node;
const device_type_t *type;
} device_t;
struct _device_type {
struct avl_node node;
device_t * (*process_config)(const char *name, struct json_object *config);
void (*free)(device_t *device);
void (*init)(device_t *device);
void (*update)(device_t *device);
void (*release)(device_t *device);
};
void register_device_type(const char *name, device_type_t *device_type);
const device_type_t * get_device_type(const char *name);

53
src/main.rs Normal file
View file

@ -0,0 +1,53 @@
#![no_main]
#![no_std]
#![feature(alloc_error_handler)]
mod system_alloc;
extern crate alloc;
use libc::cstr;
use safe_libc as libc;
use core::fmt::Write;
#[no_mangle]
pub extern "C" fn main(_nargs: libc::c_int, _args: *const *const libc::c_char) -> libc::c_int {
let mut stdout = libc::stdio::stdout();
{
let foo = cstr!("Foo!\n");
let _ = stdout.puts(foo);
}
{
let x = libc::string::CString::from("foo");
let l = x.len();
let y = x.into_raw();
let z = unsafe { alloc::boxed::Box::from_raw(core::ptr::slice_from_raw_parts_mut(y, l)) };
let _ = writeln!(stdout, "Foo: {} {} {}", z[0], z[1], z[2]);
}
{
let x = libc::string::CString::from("bar");
let _ = stdout.puts(&x);
}
{
let b = alloc::boxed::Box::new(42);
let _ = writeln!(stdout, "Bar: {}", b);
}
{
let x = alloc::boxed::Box::new(());
let _ = writeln!(stdout, "Box: {:?}", x);
}
0
}
#[cfg(not(test))]
#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! {
let _ = writeln!(libc::stdio::stderr(), "Panic: {}", info);
unsafe { libc::abort() }
}

View file

@ -1,19 +0,0 @@
src = [
'neco.c',
'config-load.c',
'config-process.c',
'device.c',
'device-bridge.c',
'device-common.c',
'device-interface.c',
'netlink.c',
'vector.c',
]
dep = [
libjson_c_dep,
libmnl_dep,
libubox_dep,
]
executable('neco', sources: src, dependencies: dep)

View file

@ -1,13 +0,0 @@
#include "config-load.h"
#include "netlink.h"
int main(int argc, char *argv[]) {
if (!nl_init())
return 1;
read_config(argv[1]);
nl_deinit();
return 0;
}

View file

@ -1,38 +0,0 @@
#include "netlink.h"
#include <stdio.h>
static struct mnl_socket *nl;
static uint32_t seq;
bool nl_init(void) {
nl = mnl_socket_open(NETLINK_ROUTE);
if (nl == NULL) {
perror("mnl_socket_open");
return false;
}
if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
perror("mnl_socket_bind");
nl_deinit();
return false;
}
return true;
}
void nl_deinit(void) {
if (!nl)
return;
mnl_socket_close(nl);
nl = NULL;
}
struct mnl_socket * nl_socket(void) {
return nl;
}
uint32_t nl_seq(void) {
return seq++;
}

View file

@ -1,12 +0,0 @@
#pragma once
#include <libmnl/libmnl.h>
#include <stdbool.h>
#include <stdbool.h>
bool nl_init(void);
void nl_deinit(void);
struct mnl_socket * nl_socket(void);
uint32_t nl_seq(void);

22
src/system_alloc.rs Normal file
View file

@ -0,0 +1,22 @@
use safe_libc as libc;
struct System;
unsafe impl alloc::alloc::GlobalAlloc for System {
unsafe fn alloc(&self, layout: alloc::alloc::Layout) -> *mut u8 {
libc::memalign(layout.align(), layout.size()).cast()
}
unsafe fn dealloc(&self, ptr: *mut u8, _layout: alloc::alloc::Layout) {
libc::free(ptr.cast());
}
}
#[global_allocator]
static SYSTEM_ALLOC: System = System;
#[cfg(not(test))]
#[alloc_error_handler]
fn alloc_error(_: core::alloc::Layout) -> ! {
panic!("allocation failure");
}

View file

@ -1,29 +0,0 @@
#pragma once
#include <json-c/json.h>
#define NODE_NAME(c) (*(char **)&(c)->node.key)
static inline struct json_object * neco_json_get_value(
struct json_object *obj,
const char *key,
enum json_type type
) {
struct json_object *value;
if (!json_object_object_get_ex(obj, key, &value))
return NULL;
if (!json_object_is_type(value, type))
return NULL;
return value;
}
static inline const char * neco_json_get_string(struct json_object *obj, const char *key) {
struct json_object *value = neco_json_get_value(obj, key, json_type_string);
if (!value)
return NULL;
return json_object_get_string(value);
}

View file

@ -1,114 +0,0 @@
/**
\file
Typesafe dynamically sized arrays
*/
#include "vector.h"
#include <errno.h>
#include <stdint.h>
#include <string.h>
/** The minimum number of elements to allocate even when fewer elements are used */
#define MIN_VECTOR_ALLOC 4
/**
Multiplies two size_t values, checking for overflows
Both arguments are limited to the size of a uint32_t (to keep the check simple)
Returns SIZE_MAX and sets errno on failure.
*/
static inline size_t mul_check(size_t members, size_t size) {
uint64_t v = (uint64_t)members * size;
if (members > UINT32_MAX || size > UINT32_MAX || v > SIZE_MAX) {
errno = EOVERFLOW;
return SIZE_MAX;
}
return v;
}
/**
Reallocates a block of memory for an array on the heap
May fail with EOVERFLOW (when members or size does not fit into a uint32_t,
or their product does not fit into a size_t) or ENOMEM.
*/
static inline void * realloc_array(void *ptr, size_t members, size_t size) {
errno = 0;
size_t array_size = mul_check(members, size);
if (errno)
return NULL;
return realloc(ptr, array_size);
}
/**
Resizes a vector
Vector allocations are always powers of 2.
Internal function, use VECTOR_RESIZE() instead.
*/
bool _vector_resize(vector_desc_t *desc, void **data, size_t n, size_t elemsize) {
desc->length = n;
size_t alloc = desc->allocated;
if (!alloc) {
alloc = MIN_VECTOR_ALLOC;
n = n*3/2;
}
while (alloc < n) {
alloc <<= 1;
if (!alloc) {
errno = ENOMEM;
return false;
}
}
if (alloc != desc->allocated) {
desc->allocated = alloc;
void *tmp = realloc_array(*data, alloc, elemsize);
if (!tmp)
return false;
*data = tmp;
}
return true;
}
/**
Inserts an element into a vector
Internal function, use VECTOR_INSERT() and VECTOR_ADD() instead.
*/
bool _vector_insert(vector_desc_t *desc, void **data, void *element, size_t pos, size_t elemsize) {
if (!_vector_resize(desc, data, desc->length+1, elemsize))
return false;
void *p = *data + pos*elemsize;
memmove(p+elemsize, p, (desc->length-pos-1)*elemsize);
memcpy(p, element, elemsize);
return true;
}
/**
Deletes an element from a vector
Internal function, use VECTOR_DELETE() instead.
*/
void _vector_delete(vector_desc_t *desc, void **data, size_t pos, size_t elemsize) {
void *p = *data + pos*elemsize;
memmove(p, p+elemsize, (desc->length-pos-1)*elemsize);
desc->length--;
}

View file

@ -1,113 +0,0 @@
/**
\file
Typesafe dynamically sized arrays
*/
#pragma once
#include <stdbool.h>
#include <stdlib.h>
/** A vector descriptor */
typedef struct _vector_desc {
size_t allocated; /**< The number of elements currently allocated */
size_t length; /**< The actual number of elements in the vector */
} vector_desc_t;
/**
A type for a vector of \e type elements
\hideinitializer
*/
#define VECTOR(type) struct { \
vector_desc_t desc; \
type *data; \
}
bool _vector_resize(vector_desc_t *desc, void **data, size_t n, size_t elemsize);
bool _vector_insert(vector_desc_t *desc, void **data, void *element, size_t pos, size_t elemsize);
void _vector_delete(vector_desc_t *desc, void **data, size_t pos, size_t elemsize);
/**
Resizes the vector \e a to \e n elements
\hideinitializer
*/
#define VECTOR_RESIZE(v, n) ({ \
__typeof__(v) *_v = &(v); \
_vector_resize(&_v->desc, (void **)&_v->data, (n), sizeof(*_v->data)); \
})
/**
Frees all resources used by the vector \e v
\hideinitializer
*/
#define VECTOR_FREE(v) free((v).data)
/**
Returns the number of elements in the vector \e v
\hideinitializer
*/
#define VECTOR_LEN(v) ((v).desc.length)
/**
Returns the element with index \e i in the vector \e v
\hideinitializer
*/
#define VECTOR_INDEX(v, i) ((v).data[i])
/**
Returns a pointer to the vector elements of \e v
\hideinitializer
*/
#define VECTOR_DATA(v) ((v).data)
/**
Inserts the element \e elem at index \e pos of vector \e v
\hideinitializer
*/
#define VECTOR_INSERT(v, elem, pos) ({ \
__typeof__(v) *_v = &(v); \
__typeof__(*_v->data) _e = (elem); \
_vector_insert(&_v->desc, (void **)&_v->data, &_e, (pos), sizeof(_e)); \
})
/**
Adds the element \e elem at the end of vector \e v
\hideinitializer
*/
#define VECTOR_ADD(v, elem) ({ \
__typeof__(v) *_v = &(v); \
__typeof__(*_v->data) _e = (elem); \
_vector_insert(&_v->desc, (void **)&_v->data, &_e, _v->desc.length, sizeof(_e)); \
})
/**
Deletes the element at index \e pos of vector \e v
\hideinitializer
*/
#define VECTOR_DELETE(v, pos) ({ \
__typeof__(v) *_v = &(v); \
_vector_delete(&_v->desc, (void **)&_v->data, (pos), sizeof(*_v->data)); \
})
/**
Performs a binary search on the vector \e v, returning a pointer to a matching vector element
\hideinitializer
*/
#define VECTOR_BSEARCH(key, v, cmp) ({ \
__typeof__(v) *_v = &(v); \
const __typeof__(*_v->data) *_key = (key); \
int (*_cmp)(__typeof__(_key), __typeof__(_key)) = (cmp); \
(__typeof__(_v->data))bsearch(_key, _v->data, _v->desc.length, sizeof(*_v->data), (int (*)(const void *, const void *))_cmp); \
})