Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
yangfl committed Sep 7, 2021
0 parents commit f55ebe8
Show file tree
Hide file tree
Showing 24 changed files with 1,483 additions and 0 deletions.
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
*.d
*.o
*.a
*.pc
.gdb_history

# C extensions
*.so*
*.out
17 changes: 17 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
108 changes: 108 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
PROJECT = inet46i

MULTIARCH ?= $(shell $(CC) --print-multiarch)

PREFIX ?= /usr
INCLUDEDIR ?= /include
LIBDIR ?= /lib/$(MULTIARCH)

DEBUG ?= 1
ifeq ($(DEBUG), 1)
RELEASE ?= 0
else
RELEASE ?= 1
endif

# default
CPPFLAGS ?= -fdiagnostics-color=always
ifeq ($(RELEASE), 0)
CFLAGS ?= -Os -fPIC
else
CFLAGS ?= -O2 -fPIC
endif
LDFLAGS ?=

# debug
ifeq ($(DEBUG), 1)
CFLAGS += -g
endif
ifeq ($(RELEASE), 0)
CPPFLAGS += -DDEBUG
endif

# hardening
CPPFLAGS += -D_FORTIFY_SOURCE=2
CFLAGS += -fstack-protector-strong
LDFLAGS += -Wl,-z,relro

# diagnosis
CWARN ?= -Wall -Wextra -Wpedantic -Werror=format-security \
-Wno-cast-function-type -Wno-missing-field-initializers
ifeq ($(DEBUG), 1)
CWARN += -fanalyzer
endif
CFLAGS += $(CWARN)

# program flags
CPPFLAGS += -D_DEFAULT_SOURCE
CFLAGS += -std=c2x -fms-extensions
LDFLAGS += -Wl,--gc-sections -flto=auto -fwhole-program

HEADERS := $(wildcard inet46i/*.h)
SOURCES := $(wildcard inet46i/*.c)
OBJS := $(sort $(SOURCES:.c=.o))
PREREQUISITES := $(SOURCES:.c=.d)
THIS_MAKEFILE_LIST := $(MAKEFILE_LIST)

LIB_NAME := lib$(PROJECT).so
SO_NAME := $(LIB_NAME).0
REAL_NAME := $(LIB_NAME).0.0

ARLIB := lib$(PROJECT).a
SHLIB := $(SO_NAME)
PCFILE := $(PROJECT).pc

.PHONY: all
all: $(ARLIB) $(SHLIB) $(PCFILE)

.PHONY: clean
clean:
rm -f $(ARLIB) $(SHLIB) $(OBJS) $(PCFILE) $(PREREQUISITES)

%.d: %.c
$(CC) -M $(CPPFLAGS) $< | sed 's,.*\.o *:,$(<:.c=.o) $@: $(THIS_MAKEFILE_LIST),' > $@

-include $(PREREQUISITES)

$(ARLIB): $(OBJS)
$(AR) rcs $@ $^

$(SHLIB): $(OBJS)
$(CC) -shared -Wl,-soname,$(SO_NAME) $(LDFLAGS) -o $@ $^

$(PCFILE): $(PCFILE).in
sed 's|@prefix@|$(PREFIX)|; s|@libdir@|$(LIBDIR)|; s|@includedir@|$(INCLUDEDIR)|' $< > $@

.PHONY: install-shared
install-shared: $(SHLIB)
install -d $(DESTDIR)$(PREFIX)$(LIBDIR) || true
install -m 0644 $< $(DESTDIR)$(PREFIX)$(LIBDIR)/$(REAL_NAME)
rm -f $(DESTDIR)$(PREFIX)$(LIBDIR)/$(SO_NAME)
ln -s $(REAL_NAME) $(DESTDIR)$(PREFIX)$(LIBDIR)/$(SO_NAME)
rm -f $(DESTDIR)$(PREFIX)$(LIBDIR)/$(LIB_NAME)
ln -s $(SO_NAME) $(DESTDIR)$(PREFIX)$(LIBDIR)/$(LIB_NAME)

.PHONY: install-static
install-static: $(ARLIB)
install -d $(DESTDIR)$(PREFIX)$(LIBDIR) || true
install -m 0644 $< $(DESTDIR)$(PREFIX)$(LIBDIR)/$(ARLIB)

.PHONY: install-header
install-header: $(HEADERS) $(PCFILE)
install -d $(DESTDIR)$(PREFIX)$(INCLUDEDIR)/$(PROJECT) || true
install -m 0644 $(HEADERS) $(DESTDIR)$(PREFIX)$(INCLUDEDIR)/$(PROJECT)/
install -d $(DESTDIR)$(PREFIX)$(LIBDIR)/pkgconfig || true
install -m 0644 $(PCFILE) $(DESTDIR)$(PREFIX)$(LIBDIR)/pkgconfig

.PHONY: install
install: install-shared install-static install-header
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# libinet46i

Use the same code to process sockets of IPv4 (`AF_INET`), IPv6 (`AF_INET6`) and the interface name (`SO_BINDTODEVICE`).

Docs to be added. Feel free to ask for it.

## Why there is a policy-based socket routing?

You may want to 'listen' to a specific interface as if an IP address, however Linux won't allow you to open two sockets binding to 0.0.0.0 listening to the same port. Routing must be done in user programs.
10 changes: 10 additions & 0 deletions inet46i.pc.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
prefix=@prefix@
libdir=${prefix}@libdir@
includedir=${prefix}@includedir@

Name: inet46i
Description: AF_INET + AF_INET6 + SO_BINDTODEVICE
Version: @version@
URL: https://github.com/yangfl/libinet46i
Libs: -L${libdir} -linet46i
Cflags: -I${includedir}/inet46i
25 changes: 25 additions & 0 deletions inet46i/endian.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#ifndef INET46I_ENDIAN_H
#define INET46I_ENDIAN_H


#define __inet46i_move_byte(type, x, from, to) \
((((type) (x) >> ((from) * 8)) & 0xff) << ((to) * 8))
#if __BYTE_ORDER == __BIG_ENDIAN
#define lhtons(x) (x)
#define lhtonl(x) (x)
#endif
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define lhtons(x) ( \
__inet46i_move_byte(uint16_t, x, 0, 1) | \
__inet46i_move_byte(uint16_t, x, 1, 0) \
)
#define lhtonl(x) ( \
__inet46i_move_byte(uint32_t, x, 0, 3) | \
__inet46i_move_byte(uint32_t, x, 1, 2) | \
__inet46i_move_byte(uint32_t, x, 2, 1) | \
__inet46i_move_byte(uint32_t, x, 3, 0) \
)
#endif


#endif /* INET46I_ENDIAN_H */
11 changes: 11 additions & 0 deletions inet46i/in46.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#include <netinet/in.h>

#include "in46.h"


extern inline int in6_normalize (struct in6_addr *addr);
extern inline int in_to6 (void *addr);
extern inline int in6_to4 (void *addr);
extern inline int in46_is_addr_unspecified (int af, void *addr);
extern inline int in64_set (int af, void *dst, const void *src);
extern inline void *in64_get (int af, const void *addr);
134 changes: 134 additions & 0 deletions inet46i/in46.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
#ifndef INET46I_IN46_H
#define INET46I_IN46_H

#include <errno.h>
#include <stddef.h>
#include <net/if.h>
#include <netinet/in.h>

#include "endian.h"


#define IN6_SET_ADDR_V4MAPPED(a) ( \
((struct in6_addr *) (a))->s6_addr32[0] = 0, \
((struct in6_addr *) (a))->s6_addr32[1] = 0, \
((struct in6_addr *) (a))->s6_addr32[2] = lhtonl(0xffff) \
)
#define IN6_IS_ADDR_V4OK(a) ( \
((struct in6_addr *) (a))->s6_addr32[0] == 0 && \
((struct in6_addr *) (a))->s6_addr32[1] == 0 && \
(((struct in6_addr *) (a))->s6_addr32[2] == 0 || \
((struct in6_addr *) (a))->s6_addr32[2] == lhtonl(0xffff)) \
)

__attribute__((nonnull))
inline int in6_normalize (struct in6_addr *addr) {
if (IN6_IS_ADDR_V4COMPAT(addr)) {
addr->s6_addr32[2] = lhtonl(0xffff);
}
if (IN6_IS_ADDR_V4MAPPED(addr)) {
if (addr->s6_addr32[3] == lhtonl(0)) {
addr->s6_addr32[2] = 0;
return 1;
}
if ((addr->s6_addr32[3] & lhtonl(IN_CLASSA_NET)) ==
lhtonl(INADDR_LOOPBACK & IN_CLASSA_NET)) {
addr->s6_addr32[2] = 0;
addr->s6_addr32[3] = lhtonl(1);
return 1;
}
}
return 0;
}


union in46_addr {
struct in_addr v4;
struct in6_addr v6;
};

__attribute__((nonnull))
inline int in_to6 (void *addr) {
((struct in6_addr *) addr)->s6_addr32[3] = ((struct in_addr *) addr)->s_addr;
IN6_SET_ADDR_V4MAPPED(addr);
return 0;
}

__attribute__((nonnull))
inline int in6_to4 (void *addr) {
if (IN6_IS_ADDR_LOOPBACK(addr)) {
((struct in_addr *) addr)->s_addr = lhtonl(INADDR_LOOPBACK);
return 0;
}
if (IN6_IS_ADDR_V4OK(addr)) {
((struct in_addr *) addr)->s_addr =
((struct in6_addr *) addr)->s6_addr32[3];
return 0;
}
return 1;
}

__attribute__((nonnull))
inline int in46_is_addr_unspecified (int af, void *addr) {
return af == AF_INET ?
((struct in_addr *) addr)->s_addr == 0 : IN6_IS_ADDR_UNSPECIFIED(addr);
}


struct in46i_addr {
union {
struct in_addr v4;
struct in6_addr v6;
uint32_t scope_id;
};
sa_family_t sa_family;
};

#define IN46I_UNSPEC {.sa_family = AF_UNSPEC}
#define IN46I_IS_UNSPEC(addr) ((addr)->sa_family == AF_UNSPEC)


struct in64_addr {
union {
struct in6_addr addr6;
struct {
uint32_t __u32_addr_padding[3];
struct in_addr addr;
};
};
uint32_t scope_id;
};

#define IN64_IS_ADDR_UNSPECIFIED(a) ( \
IN6_IS_ADDR_V4OK(a) && ((struct in6_addr *) (a))->s6_addr32[3] == 0)

__attribute__((nonnull, access(write_only, 2), access(read_only, 3)))
inline int in64_set (int af, void *dst, const void *src) {
switch (af) {
case AF_INET:
((struct in64_addr *) dst)->addr.s_addr = *(in_addr_t *) src;
IN6_SET_ADDR_V4MAPPED(dst);
break;
case AF_INET6: {
struct in6_addr *dst_ = dst;
const struct in6_addr *src_ = src;
dst_->s6_addr32[0] = src_->s6_addr32[0];
dst_->s6_addr32[1] = src_->s6_addr32[1];
dst_->s6_addr32[2] = src_->s6_addr32[2];
dst_->s6_addr32[3] = src_->s6_addr32[3];
break;
}
default:
errno = EAFNOSUPPORT;
}
return 0;
}

__attribute__((nonnull, access(read_only, 2)))
inline void *in64_get (int af, const void *addr) {
return af == AF_INET ?
(void *) &((struct in64_addr *) addr)->addr : (void *) addr;
}


#endif /* INET46I_IN46_H */
Loading

0 comments on commit f55ebe8

Please sign in to comment.