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
commit 14506665d9
21 changed files with 956 additions and 0 deletions

23
.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
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
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
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

1
include/Makefile.am Normal file
View File

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

View File

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

26
include/libgtpnl/gtpnl.h Normal file
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

45
include/linux/gtp_nl.h Normal file
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
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}

21
m4/gcc4_visibility.m4 Normal file
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
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

146
src/genl.c Normal file
View File

@ -0,0 +1,146 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#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)
{
struct nlmsghdr *nlh;
struct genlmsghdr *genl;
nlh = mnl_nlmsg_put_header(buf);
nlh->nlmsg_type = type;
nlh->nlmsg_flags = NLM_F_REQUEST | flags;
nlh->nlmsg_seq = seq;
genl = mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr));
genl->cmd = cmd;
genl->version = 0;
return nlh;
}
EXPORT_SYMBOL(genl_nlmsg_build_hdr);
static int genl_ctrl_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 CTRL_ATTR_FAMILY_ID:
if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0) {
perror("mnl_attr_validate");
return MNL_CB_ERROR;
}
break;
}
tb[type] = attr;
return MNL_CB_OK;
}
static int genl_ctrl_cb(const struct nlmsghdr *nlh, void *data)
{
struct nlattr *tb[CTRL_ATTR_MAX + 1] = {};
struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
int32_t *genl_id = data;
mnl_attr_parse(nlh, sizeof(*genl), genl_ctrl_validate_cb, tb);
if (tb[CTRL_ATTR_FAMILY_ID])
*genl_id = mnl_attr_get_u16(tb[CTRL_ATTR_FAMILY_ID]);
else
*genl_id = -1;
return MNL_CB_OK;
}
struct mnl_socket *genl_socket_open(void)
{
struct mnl_socket *nl;
nl = mnl_socket_open(NETLINK_GENERIC);
if (nl == NULL) {
perror("mnl_socket_open");
return NULL;
}
if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
perror("mnl_socket_bind");
return NULL;
}
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),
void *data)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
int ret;
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
perror("mnl_socket_send");
return -1;
}
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
while (ret > 0) {
ret = mnl_cb_run(buf, ret, seq, mnl_socket_get_portid(nl),
cb, data);
if (ret <= 0)
break;
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
}
return ret;
}
EXPORT_SYMBOL(genl_socket_talk);
static struct nlmsghdr *
genl_nlmsg_build_lookup(char *buf, const char *subsys_name)
{
struct nlmsghdr *nlh;
struct genlmsghdr *genl;
nlh = mnl_nlmsg_put_header(buf);
nlh->nlmsg_type = GENL_ID_CTRL;
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
nlh->nlmsg_seq = time(NULL);
genl = mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr));
genl->cmd = CTRL_CMD_GETFAMILY;
genl->version = 1;
mnl_attr_put_u32(nlh, CTRL_ATTR_FAMILY_ID, GENL_ID_CTRL);
mnl_attr_put_strz(nlh, CTRL_ATTR_FAMILY_NAME, subsys_name);
return nlh;
}
int genl_lookup_family(struct mnl_socket *nl, const char *family)
{
int32_t genl_id;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh = genl_nlmsg_build_lookup(buf, "gtp");
int err;
err = genl_socket_talk(nl, nlh, nlh->nlmsg_seq, genl_ctrl_cb, &genl_id);
if (err < 0)
return -1;
return genl_id;
}
EXPORT_SYMBOL(genl_lookup_family);

177
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
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
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
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
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}

82
tools/gtp-link-add.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 <linux/if.h>
#include <linux/if_link.h>
#include <linux/rtnetlink.h>
#include <linux/gtp_nl.h>
int main(int argc, char *argv[])
{
struct mnl_socket *nl;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
struct ifinfomsg *ifm;
int ret;
unsigned int seq, portid, change = 0, flags = 0;
struct nlattr *nest, *nest2;
if (argc != 2) {
printf("Usage: %s [ifname]\n", argv[0]);
exit(EXIT_FAILURE);
}
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(argv[1]));
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");
exit(EXIT_FAILURE);
}
if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
perror("mnl_socket_bind");
exit(EXIT_FAILURE);
}
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");
exit(EXIT_FAILURE);
}
ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
if (ret == -1) {
perror("read");
exit(EXIT_FAILURE);
}
ret = mnl_cb_run(buf, ret, seq, portid, NULL, NULL);
if (ret == -1){
perror("callback");
exit(EXIT_FAILURE);
}
mnl_socket_close(nl);
return 0;
}

214
tools/gtp-tunnel.c Normal file
View File

@ -0,0 +1,214 @@
#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 <libmnl/libmnl.h>
#include <linux/genetlink.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)
{
uint32_t gtp_ifidx;
struct in_addr ms, sgsn;
struct nlmsghdr *nlh;
char buf[MNL_SOCKET_BUFFER_SIZE];
uint32_t seq = time(NULL), gtp_version;
if (argc != 7) {
printf("%s add <gtp device> <v0|v1> <tid> <ms-addr> <sgsn-addr>\n",
argv[0]);
return EXIT_FAILURE;
}
gtp_ifidx = if_nametoindex(argv[2]);
if (gtp_ifidx == 0) {
fprintf(stderr, "wrong GTP interface %s\n", argv[2]);
return EXIT_FAILURE;
}
if (inet_aton(argv[5], &ms) < 0) {
perror("bad address for ms");
exit(EXIT_FAILURE);
}
if (inet_aton(argv[6], &sgsn) < 0) {
perror("bad address for sgsn");
exit(EXIT_FAILURE);
}
if (strcmp(argv[3], "v0") == 0)
gtp_version = GTP_V0;
else if (strcmp(argv[3], "v1") == 0)
gtp_version = GTP_V1;
else {
fprintf(stderr, "wrong GTP version %s, use v0 or v1\n",
argv[3]);
return EXIT_FAILURE;
}
nlh = genl_nlmsg_build_hdr(buf, genl_id, NLM_F_EXCL | NLM_F_ACK, ++seq,
GTP_CMD_TUNNEL_NEW);
gtp_build_payload(nlh, atoi(argv[4]), 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;
}
static int
del_tunnel(int argc, char *argv[], int genl_id, struct mnl_socket *nl)
{
uint32_t gtp_ifidx;
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
uint32_t seq = time(NULL);
if (argc != 5) {
printf("%s add <gtp device> <version> <tid>\n",
argv[0]);
return EXIT_FAILURE;
}
gtp_ifidx = if_nametoindex(argv[2]);
nlh = genl_nlmsg_build_hdr(buf, genl_id, NLM_F_ACK, ++seq,
GTP_CMD_TUNNEL_DELETE);
gtp_build_payload(nlh, atoi(argv[4]), gtp_ifidx, 0, 0, atoi(argv[3]));
if (genl_socket_talk(nl, nlh, seq, NULL, NULL) < 0)
perror("genl_socket_talk");
return 0;
}
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 %llu ms_addr %s ", pdp.tid, inet_ntoa(pdp.sgsn_addr));
printf("sgsn_addr %s\n", inet_ntoa(pdp.ms_addr));
return MNL_CB_OK;
}
static int
list_tunnel(int argc, char *argv[], 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;
}
int main(int argc, char *argv[])
{
struct mnl_socket *nl;
char buf[MNL_SOCKET_BUFFER_SIZE];
unsigned int portid;
int32_t genl_id;
int ret;
if (argc < 2) {
printf("%s <add|delete|list> [<options,...>]\n", argv[0]);
exit(EXIT_FAILURE);
}
nl = genl_socket_open();
if (nl == NULL) {
perror("mnl_socket_open");
exit(EXIT_FAILURE);
}
genl_id = genl_lookup_family(nl, "gtp");
if (genl_id < 0) {
printf("not found gtp genl family\n");
exit(EXIT_FAILURE);
}
if (strncmp(argv[1], "add", strlen(argv[1])) == 0)
ret = add_tunnel(argc, argv, genl_id, nl);
else if (strncmp(argv[1], "delete", strlen(argv[1])) == 0)
ret = del_tunnel(argc, argv, genl_id, nl);
else if (strncmp(argv[1], "list", strlen(argv[1])) == 0)
ret = list_tunnel(argc, argv, genl_id, nl);
else {
printf("Unknown command `%s'\n", argv[1]);
exit(EXIT_FAILURE);
}
mnl_socket_close(nl);
return ret;
}

13
tools/gtpnl.c Normal file
View File

@ -0,0 +1,13 @@
#include <stdint.h>
#include <libmnl/libmnl.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)
{
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);
}