add libgtpnl

This patch adds the libgtpnl library. Harald mentioned that he wanted
that the specific code that is added is well encapsulated, so let's
start a small library to interact with the GTP kernel module via netlink
interface.

This was done a bit while in the rush, so the interfaces are not nice
at all and the tools need to be ported on top of this library.

This library will be used to integrate openggsn with the GTP kernel
module.
This commit is contained in:
Pablo Neira Ayuso 2014-02-20 18:43:15 +01:00 committed by Pablo Neira Ayuso
parent a3ab375c9a
commit c7e6ffadb4
22 changed files with 513 additions and 7 deletions

View File

@ -5,8 +5,5 @@ obj-m += gtp.o
default:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
$(CC) -lmnl gtp-link-add.c -o gtp-link-add
$(CC) -lmnl gtp-tunnel.c genl.c gtpnl.c -o gtp-tunnel
clean:
rm -rf *.o *.mod.* modules.order Module.symvers *.ko .tmp_versions .*.cmd
rm -f genl-family-get gtp-link-add gtp-tunnel-add

23
libgtnl/.gitignore vendored Normal file
View File

@ -0,0 +1,23 @@
# Dependency and object files
.*.d
*.o
*.lo
*.la
.deps
.libs
.dirstamp
# Generated by autoconf/configure
Makefile
Makefile.in
config.*
configure
autom4te.cache
stamp-h1
aclocal.m4
libgtpnl.pc
libtool
build-aux
# Debian package build temporary files
build-stamp

24
libgtnl/Make_global.am Normal file
View File

@ -0,0 +1,24 @@
# This is _NOT_ the library release version, it's an API version.
# Extracted from Chapter 6 "Library interface versions" of the libtool docs.
#
# <snippet>
# Here are a set of rules to help you update your library version information:
#
# 1. Start with version information of `0:0:0' for each libtool library.
# 2. Update the version information only immediately before a public release
# of your software. More frequent updates are unnecessary, and only guarantee
# that the current interface number gets larger faster.
# 3. If the library source code has changed at all since the last update,
# then increment revision (`c:r:a' becomes `c:r+1:a').
# 4. If any interfaces have been added, removed, or changed since the last
# update, increment current, and set revision to 0.
# 5. If any interfaces have been added since the last public release, then
# increment age.
# 6. If any interfaces have been removed since the last public release, then
# set age to 0.
# </snippet>
#
LIBVERSION=0:0:0
AM_CPPFLAGS = ${regular_CPPFLAGS} -I${top_srcdir}/include ${LIBMNL_CFLAGS}
AM_CFLAGS = ${regular_CFLAGS} ${GCC_FVISIBILITY_HIDDEN}

11
libgtnl/Makefile.am Normal file
View File

@ -0,0 +1,11 @@
include $(top_srcdir)/Make_global.am
ACLOCAL_AMFLAGS = -I m4
SUBDIRS = src include tools
DIST_SUBDIRS = src include tools
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libgtpnl.pc
${pkgconfig_DATA}: ${top_builddir}/config.status

34
libgtnl/configure.ac Normal file
View File

@ -0,0 +1,34 @@
dnl Process this file with autoconf to create configure.
AC_INIT([libgtpnl], [1.0.0])
AC_CONFIG_AUX_DIR([build-aux])
AC_CANONICAL_HOST
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_HEADERS([config.h])
AM_INIT_AUTOMAKE([foreign tar-pax no-dist-gzip dist-bzip2 1.6 subdir-objects])
dnl kernel style compile messages
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
dnl Dependencies
PKG_CHECK_MODULES([LIBMNL], [libmnl >= 1.0.0])
AC_PROG_CC
AM_PROG_CC_C_O
AC_EXEEXT
AC_DISABLE_STATIC
LT_INIT
CHECK_GCC_FVISIBILITY
case "$host" in
*-*-linux* | *-*-uclinux*) ;;
*) AC_MSG_ERROR([Linux only, dude!]);;
esac
regular_CPPFLAGS="-D_FILE_OFFSET_BITS=64 -D_REENTRANT"
regular_CFLAGS="-Wall -Waggregate-return -Wmissing-declarations \
-Wmissing-prototypes -Wshadow -Wstrict-prototypes \
-Wformat=2 -pipe"
AC_SUBST([regular_CPPFLAGS])
AC_SUBST([regular_CFLAGS])
AC_CONFIG_FILES([Makefile src/Makefile include/Makefile include/libgtpnl/Makefile include/linux/Makefile tools/Makefile libgtpnl.pc])
AC_OUTPUT

View File

@ -0,0 +1 @@
SUBDIRS = libgtpnl linux

View File

@ -0,0 +1 @@
pkginclude_HEADERS = gtpnl.h

View File

@ -0,0 +1,26 @@
#ifndef _LIBGTPNL_H_
#define _LIBGTPNL_H_
#include <stdint.h>
struct mnl_socket;
struct nlmsghdr;
struct mnl_socket *genl_socket_open(void);
struct nlmsghdr *genl_nlmsg_build_hdr(char *buf, uint16_t type, uint16_t flags,
uint32_t seq, uint8_t cmd);
int genl_socket_talk(struct mnl_socket *nl, struct nlmsghdr *nlh, uint32_t seq,
int (*cb)(const struct nlmsghdr *nlh, void *data),
void *data);
int genl_lookup_family(struct mnl_socket *nl, const char *family);
int gtp_dev_create(const char *ifname);
int gtp_add_tunnel(int genl_id, struct mnl_socket *nl, const char *ifname,
const char *ms_addr, const char *sgsn_addr, uint64_t tid,
int gtp_version);
int gtp_del_tunnel(int genl_id, struct mnl_socket *nl, const char *ifname,
uint64_t tid, uint32_t gtp_version);
int gtp_list_tunnel(int genl_id, struct mnl_socket *nl);
#endif

View File

@ -0,0 +1 @@
noinst_HEADERS = gtp_nl.h

View File

@ -0,0 +1,45 @@
#ifndef _UAPI_LINUX_GTP_H_
#define _UAPI_LINUX_GTP_H__
enum {
IFLA_GTP_UNSPEC,
IFLA_GTP_LOCAL_ADDR_IPV4,
IFLA_GTP_HASHSIZE,
__IFLA_GTP_MAX,
};
#define IFLA_GTP_MAX (__IFLA_GTP_MAX - 1)
enum gtp_genl_cmds {
GTP_CMD_TUNNEL_NEW,
GTP_CMD_TUNNEL_DELETE,
GTP_CMD_TUNNEL_GET,
GTP_CMD_TUNNEL_MAX,
};
enum gtp_version {
GTP_V0 = 0,
GTP_V1,
};
enum gtp_cfg_attrs {
GTPA_CFG_UNSPEC = 0,
GTPA_CFG_LINK,
GTPA_CFG_LOCAL_ADDR_IPV4,
GTPA_CFG_HSIZE,
__GTPA_CFG_MAX,
};
#define GTPA_CFG_MAX (__GTPA_CFG_MAX + 1)
enum gtp_attrs {
GTPA_UNSPEC = 0,
GTPA_LINK,
GTPA_VERSION,
GTPA_TID, /* 64 bits for GTPv1 */
GTPA_SGSN_ADDRESS,
GTPA_MS_ADDRESS,
__GTPA_MAX,
};
#define GTPA_MAX (__GTPA_MAX + 1)
#endif /* _UAPI_LINUX_GTP_H_ */

15
libgtnl/libgtpnl.pc.in Normal file
View File

@ -0,0 +1,15 @@
# libgtpnl pkg-config file
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: libgtpnl
Description: Netlink library for GTP Linux kernel implementation
URL: http://git.osmocom.org/libgtpnl/
Version: @VERSION@
Requires:
Conflicts:
Libs: -L${libdir} -lgtpnl
Cflags: -I${includedir}

View File

@ -0,0 +1,21 @@
# GCC 4.x -fvisibility=hidden
AC_DEFUN([CHECK_GCC_FVISIBILITY], [
AC_LANG_PUSH([C])
saved_CFLAGS="$CFLAGS"
CFLAGS="$saved_CFLAGS -fvisibility=hidden"
AC_CACHE_CHECK([whether compiler accepts -fvisibility=hidden],
[ac_cv_fvisibility_hidden], AC_COMPILE_IFELSE(
[AC_LANG_SOURCE()],
[ac_cv_fvisibility_hidden=yes],
[ac_cv_fvisibility_hidden=no]
))
if test "$ac_cv_fvisibility_hidden" = "yes"; then
AC_DEFINE([HAVE_VISIBILITY_HIDDEN], [1],
[True if compiler supports -fvisibility=hidden])
AC_SUBST([GCC_FVISIBILITY_HIDDEN], [-fvisibility=hidden])
fi
CFLAGS="$saved_CFLAGS"
AC_LANG_POP([C])
])

5
libgtnl/src/Makefile.am Normal file
View File

@ -0,0 +1,5 @@
include $(top_srcdir)/Make_global.am
lib_LTLIBRARIES = libgtpnl.la
libgtpnl_la_LDFLAGS = -Wl,--version-script=$(srcdir)/libgtpnl.map -version-info $(LIBVERSION)
libgtpnl_la_SOURCES = genl.c gtp-genl.c gtp-rtnl.c libgtpnl.map

View File

@ -6,6 +6,10 @@
#include <libmnl/libmnl.h>
#include <linux/genetlink.h>
#include <libgtpnl/gtpnl.h>
#include "internal.h"
struct nlmsghdr *
genl_nlmsg_build_hdr(char *buf, uint16_t type, uint16_t flags, uint32_t seq,
uint8_t cmd)
@ -24,6 +28,7 @@ genl_nlmsg_build_hdr(char *buf, uint16_t type, uint16_t flags, uint32_t seq,
return nlh;
}
EXPORT_SYMBOL(genl_nlmsg_build_hdr);
static int genl_ctrl_validate_cb(const struct nlattr *attr, void *data)
{
@ -77,6 +82,7 @@ struct mnl_socket *genl_socket_open(void)
return nl;
}
EXPORT_SYMBOL(genl_socket_open);
int genl_socket_talk(struct mnl_socket *nl, struct nlmsghdr *nlh, uint32_t seq,
int (*cb)(const struct nlmsghdr *nlh, void *data),
@ -101,6 +107,7 @@ int genl_socket_talk(struct mnl_socket *nl, struct nlmsghdr *nlh, uint32_t seq,
return ret;
}
EXPORT_SYMBOL(genl_socket_talk);
static struct nlmsghdr *
genl_nlmsg_build_lookup(char *buf, const char *subsys_name)
@ -136,3 +143,4 @@ int genl_lookup_family(struct mnl_socket *nl, const char *family)
return genl_id;
}
EXPORT_SYMBOL(genl_lookup_family);

177
libgtnl/src/gtp-genl.c Normal file
View File

@ -0,0 +1,177 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <inttypes.h>
#include <libmnl/libmnl.h>
#include <linux/genetlink.h>
#include <libgtpnl/gtpnl.h>
#include <net/if.h>
#include <linux/gtp_nl.h>
#include "internal.h"
static void gtp_build_payload(struct nlmsghdr *nlh, uint64_t tid,
uint32_t ifidx, uint32_t sgsn_addr,
uint32_t ms_addr, uint32_t version)
{
mnl_attr_put_u32(nlh, GTPA_VERSION, version);
mnl_attr_put_u32(nlh, GTPA_LINK, ifidx);
mnl_attr_put_u32(nlh, GTPA_SGSN_ADDRESS, sgsn_addr);
mnl_attr_put_u32(nlh, GTPA_MS_ADDRESS, ms_addr);
mnl_attr_put_u64(nlh, GTPA_TID, tid);
}
int gtp_add_tunnel(int genl_id, struct mnl_socket *nl, const char *ifname,
const char *ms_addr, const char *sgsn_addr, uint64_t tid,
int gtp_version)
{
uint32_t gtp_ifidx;
struct in_addr ms, sgsn;
struct nlmsghdr *nlh;
char buf[MNL_SOCKET_BUFFER_SIZE];
uint32_t seq = time(NULL);
gtp_ifidx = if_nametoindex(ifname);
if (gtp_ifidx == 0){
fprintf(stderr, "wrong GTP interface %s\n", ifname);
return -1;
}
if (inet_aton(ms_addr, &ms) < 0) {
perror("bad address for ms");
return -1;
}
if (inet_aton(sgsn_addr, &sgsn) < 0) {
perror("bad address for sgsn");
return -1;
}
if (gtp_version > GTP_V1) {
fprintf(stderr, "wrong GTP version %u, use v0 or v1\n",
gtp_version);
return -1;
}
nlh = genl_nlmsg_build_hdr(buf, genl_id, NLM_F_EXCL | NLM_F_ACK, ++seq,
GTP_CMD_TUNNEL_NEW);
gtp_build_payload(nlh, tid, gtp_ifidx, sgsn.s_addr,
ms.s_addr, gtp_version);
if (genl_socket_talk(nl, nlh, seq, NULL, NULL) < 0)
perror("genl_socket_talk");
return 0;
}
EXPORT_SYMBOL(gtp_add_tunnel);
int gtp_del_tunnel(int genl_id, struct mnl_socket *nl, const char *ifname,
uint64_t tid, uint32_t gtp_version)
{
uint32_t gtp_ifidx;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
uint32_t seq = time(NULL);
gtp_ifidx = if_nametoindex(ifname);
nlh = genl_nlmsg_build_hdr(buf, genl_id, NLM_F_ACK, ++seq,
GTP_CMD_TUNNEL_DELETE);
gtp_build_payload(nlh, tid, gtp_ifidx, 0, 0, gtp_version);
if (genl_socket_talk(nl, nlh, seq, NULL, NULL) < 0)
perror("genl_socket_talk");
return 0;
}
EXPORT_SYMBOL(gtp_del_tunnel);
struct gtp_pdp {
uint32_t version;
uint64_t tid;
struct in_addr sgsn_addr;
struct in_addr ms_addr;
};
static int genl_gtp_validate_cb(const struct nlattr *attr, void *data)
{
const struct nlattr **tb = data;
int type = mnl_attr_get_type(attr);
if (mnl_attr_type_valid(attr, CTRL_ATTR_MAX) < 0)
return MNL_CB_OK;
switch(type) {
case GTPA_TID:
if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0) {
perror("mnl_attr_validate");
return MNL_CB_ERROR;
}
break;
case GTPA_SGSN_ADDRESS:
case GTPA_MS_ADDRESS:
case GTPA_VERSION:
if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
perror("mnl_attr_validate");
return MNL_CB_ERROR;
}
break;
default:
break;
}
tb[type] = attr;
return MNL_CB_OK;
}
static int genl_gtp_attr_cb(const struct nlmsghdr *nlh, void *data)
{
struct nlattr *tb[GTPA_MAX + 1] = {};
struct gtp_pdp pdp;
struct genlmsghdr *genl;
mnl_attr_parse(nlh, sizeof(*genl), genl_gtp_validate_cb, tb);
if (tb[GTPA_TID])
pdp.tid = mnl_attr_get_u64(tb[GTPA_TID]);
if (tb[GTPA_SGSN_ADDRESS]) {
pdp.sgsn_addr.s_addr =
mnl_attr_get_u32(tb[GTPA_SGSN_ADDRESS]);
}
if (tb[GTPA_MS_ADDRESS]) {
pdp.ms_addr.s_addr = mnl_attr_get_u32(tb[GTPA_MS_ADDRESS]);
}
if (tb[GTPA_VERSION]) {
pdp.version = mnl_attr_get_u32(tb[GTPA_VERSION]);
}
printf("version %u ", pdp.version);
printf("tid %"PRIu64" ms_addr %s ", pdp.tid, inet_ntoa(pdp.sgsn_addr));
printf("sgsn_addr %s\n", inet_ntoa(pdp.ms_addr));
return MNL_CB_OK;
}
int gtp_list_tunnel(int genl_id, struct mnl_socket *nl)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
uint32_t seq = time(NULL);
nlh = genl_nlmsg_build_hdr(buf, genl_id, NLM_F_DUMP, 0,
GTP_CMD_TUNNEL_GET);
if (genl_socket_talk(nl, nlh, seq, genl_gtp_attr_cb, NULL) < 0) {
perror("genl_socket_talk");
return 0;
}
return 0;
}
EXPORT_SYMBOL(gtp_list_tunnel);

82
libgtnl/src/gtp-rtnl.c Normal file
View File

@ -0,0 +1,82 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <libmnl/libmnl.h>
#include <net/if.h>
#include <linux/if_link.h>
#include <linux/rtnetlink.h>
#include <libgtpnl/gtpnl.h>
#include <linux/gtp_nl.h>
#include "internal.h"
int gtp_dev_create(const char *ifname)
{
struct mnl_socket *nl;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
struct ifinfomsg *ifm;
int ret;
unsigned int seq, portid;
struct nlattr *nest, *nest2;
nlh = mnl_nlmsg_put_header(buf);
nlh->nlmsg_type = RTM_NEWLINK;
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK;
nlh->nlmsg_seq = seq = time(NULL);
ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm));
ifm->ifi_family = AF_INET;
ifm->ifi_change |= IFF_UP;
ifm->ifi_flags |= IFF_UP;
mnl_attr_put_u32(nlh, IFLA_LINK, if_nametoindex(ifname));
nest = mnl_attr_nest_start(nlh, IFLA_LINKINFO);
mnl_attr_put_str(nlh, IFLA_INFO_KIND, "gtp");
nest2 = mnl_attr_nest_start(nlh, IFLA_INFO_DATA);
mnl_attr_put_u32(nlh, IFLA_GTP_LOCAL_ADDR_IPV4, 0);
mnl_attr_put_u32(nlh, IFLA_GTP_HASHSIZE, 131072);
mnl_attr_nest_end(nlh, nest2);
mnl_attr_nest_end(nlh, nest);
nl = mnl_socket_open(NETLINK_ROUTE);
if (nl == NULL) {
perror("mnl_socket_open");
return -1;
}
if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
perror("mnl_socket_bind");
return -1;
}
portid = mnl_socket_get_portid(nl);
mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len,
sizeof(struct ifinfomsg));
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
perror("mnl_socket_send");
return -1;
}
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
if (ret == -1) {
perror("read");
return -1;
}
ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL);
if (ret == -1){
perror("callback");
return -1;
}
mnl_socket_close(nl);
return 0;
}
EXPORT_SYMBOL(gtp_dev_create);

12
libgtnl/src/internal.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef INTERNAL_H
#define INTERNAL_H 1
#include "config.h"
#ifdef HAVE_VISIBILITY_HIDDEN
# define __visible __attribute__((visibility("default")))
# define EXPORT_SYMBOL(x) typeof(x) (x) __visible
#else
# define EXPORT_SYMBOL
#endif
#endif

13
libgtnl/src/libgtpnl.map Normal file
View File

@ -0,0 +1,13 @@
LIBGTPNL_1.0 {
global:
genl_socket_open;
genl_nlmsg_build_hdr;
genl_socket_talk;
genl_lookup_family;
gtp_dev_create;
gtp_add_tunnel;
gtp_del_tunnel;
gtp_list_tunnel;
local: *;
};

10
libgtnl/tools/Makefile.am Normal file
View File

@ -0,0 +1,10 @@
include $(top_srcdir)/Make_global.am
check_PROGRAMS = gtp-link-add \
gtp-tunnel
gtp_link_add_SOURCES = gtp-link-add.c gtpnl.c
gtp_link_add_LDADD = ../src/libgtpnl.la ${LIBMNL_LIBS}
gtp_tunnel_SOURCES = gtp-tunnel.c gtpnl.c
gtp_tunnel_LDADD = ../src/libgtpnl.la ${LIBMNL_LIBS}

View File

@ -9,7 +9,7 @@
#include <linux/if_link.h>
#include <linux/rtnetlink.h>
#include "gtp_nl.h"
#include <linux/gtp_nl.h>
int main(int argc, char *argv[])
{

View File

@ -10,8 +10,8 @@
#include <libmnl/libmnl.h>
#include <linux/genetlink.h>
#include "gtp_nl.h"
#include "genl.h"
#include <linux/gtp_nl.h>
#include <libgtpnl/gtpnl.h>
static int
add_tunnel(int argc, char *argv[], int genl_id, struct mnl_socket *nl)

View File

@ -1,6 +1,6 @@
#include <stdint.h>
#include <libmnl/libmnl.h>
#include "gtp_nl.h"
#include <linux/gtp_nl.h>
void gtp_build_payload(struct nlmsghdr *nlh, uint64_t tid, uint32_t ifidx,
uint32_t sgsn_addr, uint32_t ms_addr, uint32_t version)