Exemplo n.º 1
0
static void
nft_rule_expr_bitwise_build(struct nlmsghdr *nlh, struct nft_rule_expr *e)
{
	struct nft_expr_bitwise *bitwise = nft_expr_data(e);

	if (e->flags & (1 << NFT_EXPR_BITWISE_SREG))
		mnl_attr_put_u32(nlh, NFTA_BITWISE_SREG, htonl(bitwise->sreg));
	if (e->flags & (1 << NFT_EXPR_BITWISE_DREG))
		mnl_attr_put_u32(nlh, NFTA_BITWISE_DREG, htonl(bitwise->dreg));
	if (e->flags & (1 << NFT_EXPR_BITWISE_LEN))
		mnl_attr_put_u32(nlh, NFTA_BITWISE_LEN, htonl(bitwise->len));
	if (e->flags & (1 << NFT_EXPR_BITWISE_MASK)) {
		struct nlattr *nest;

		nest = mnl_attr_nest_start(nlh, NFTA_BITWISE_MASK);
		mnl_attr_put(nlh, NFTA_DATA_VALUE, bitwise->mask.len,
				bitwise->mask.val);
		mnl_attr_nest_end(nlh, nest);
	}
	if (e->flags & (1 << NFT_EXPR_BITWISE_XOR)) {
		struct nlattr *nest;

		nest = mnl_attr_nest_start(nlh, NFTA_BITWISE_XOR);
		mnl_attr_put(nlh, NFTA_DATA_VALUE, bitwise->xor.len,
				bitwise->xor.val);
		mnl_attr_nest_end(nlh, nest);
	}
}
Exemplo n.º 2
0
static int cmd_link_set_prop(struct nlmsghdr *nlh, const struct cmd *cmd,
			     struct cmdl *cmdl, void *data)
{
	int val;
	int prop;
	char buf[MNL_SOCKET_BUFFER_SIZE];
	struct nlattr *props;
	struct nlattr *attrs;
	struct opt *opt;
	struct opt opts[] = {
		{ "link",		OPT_KEYVAL,	NULL },
		{ NULL }
	};

	if (strcmp(cmd->cmd, PRIORITY_STR) == 0)
		prop = TIPC_NLA_PROP_PRIO;
	else if ((strcmp(cmd->cmd, TOLERANCE_STR) == 0))
		prop = TIPC_NLA_PROP_TOL;
	else if ((strcmp(cmd->cmd, WINDOW_STR) == 0))
		prop = TIPC_NLA_PROP_WIN;
	else
		return -EINVAL;

	if (help_flag) {
		(cmd->help)(cmdl);
		return -EINVAL;
	}

	if (cmdl->optind >= cmdl->argc) {
		fprintf(stderr, "error, missing value\n");
		return -EINVAL;
	}
	val = atoi(shift_cmdl(cmdl));

	if (parse_opts(opts, cmdl) < 0)
		return -EINVAL;

	nlh = msg_init(buf, TIPC_NL_LINK_SET);
	if (!nlh) {
		fprintf(stderr, "error, message initialisation failed\n");
		return -1;
	}
	attrs = mnl_attr_nest_start(nlh, TIPC_NLA_LINK);

	opt = get_opt(opts, "link");
	if (!opt) {
		fprintf(stderr, "error, missing link\n");
		return -EINVAL;
	}
	mnl_attr_put_strz(nlh, TIPC_NLA_LINK_NAME, opt->val);

	props = mnl_attr_nest_start(nlh, TIPC_NLA_LINK_PROP);
	mnl_attr_put_u32(nlh, prop, val);
	mnl_attr_nest_end(nlh, props);

	mnl_attr_nest_end(nlh, attrs);

	return msg_doit(nlh, link_get_cb, &prop);
}
Exemplo n.º 3
0
static int cmd_link_mon_set_prop(struct nlmsghdr *nlh, const struct cmd *cmd,
				 struct cmdl *cmdl, void *data)
{
	int size;
	char buf[MNL_SOCKET_BUFFER_SIZE];
	struct nlattr *attrs;

	if (cmdl->argc != cmdl->optind + 1) {
		fprintf(stderr, "error, missing value\n");
		return -EINVAL;
	}
	size = atoi(shift_cmdl(cmdl));

	nlh = msg_init(buf, TIPC_NL_MON_SET);
	if (!nlh) {
		fprintf(stderr, "error, message initialisation failed\n");
		return -1;
	}
	attrs = mnl_attr_nest_start(nlh, TIPC_NLA_MON);

	mnl_attr_put_u32(nlh, TIPC_NLA_MON_ACTIVATION_THRESHOLD, size);

	mnl_attr_nest_end(nlh, attrs);

	return msg_doit(nlh, NULL, NULL);
}
Exemplo n.º 4
0
static int cmd_node_set_netid(struct nlmsghdr *nlh, const struct cmd *cmd,
                              struct cmdl *cmdl, void *data)
{
    int netid;
    char buf[MNL_SOCKET_BUFFER_SIZE];
    struct nlattr *nest;

    if (help_flag) {
        (cmd->help)(cmdl);
        return -EINVAL;
    }

    if (!(nlh = msg_init(buf, TIPC_NL_NET_SET))) {
        fprintf(stderr, "error, message initialisation failed\n");
        return -1;
    }

    if (cmdl->argc != cmdl->optind + 1) {
        fprintf(stderr, "Usage: %s node set netid NETID\n",
                cmdl->argv[0]);
        return -EINVAL;
    }
    netid = atoi(shift_cmdl(cmdl));

    nest = mnl_attr_nest_start(nlh, TIPC_NLA_NET);
    mnl_attr_put_u32(nlh, TIPC_NLA_NET_ID, netid);
    mnl_attr_nest_end(nlh, nest);

    return msg_doit(nlh, NULL, NULL);
}
Exemplo n.º 5
0
static int cmd_node_set_addr(struct nlmsghdr *nlh, const struct cmd *cmd,
                             struct cmdl *cmdl, void *data)
{
    char *str;
    uint32_t addr;
    struct nlattr *nest;
    char buf[MNL_SOCKET_BUFFER_SIZE];

    if (cmdl->argc != cmdl->optind + 1) {
        fprintf(stderr, "Usage: %s node set address ADDRESS\n",
                cmdl->argv[0]);
        return -EINVAL;
    }

    str = shift_cmdl(cmdl);
    addr = str2addr(str);
    if (!addr)
        return -1;

    if (!(nlh = msg_init(buf, TIPC_NL_NET_SET))) {
        fprintf(stderr, "error, message initialisation failed\n");
        return -1;
    }

    nest = mnl_attr_nest_start(nlh, TIPC_NLA_NET);
    mnl_attr_put_u32(nlh, TIPC_NLA_NET_ADDR, addr);
    mnl_attr_nest_end(nlh, nest);

    return msg_doit(nlh, NULL, NULL);
}
Exemplo n.º 6
0
static int
nfct_build_tuple_ip(struct nlmsghdr *nlh, const struct __nfct_tuple *t)
{
	struct nlattr *nest;

	nest = mnl_attr_nest_start(nlh, CTA_TUPLE_IP);
	if (nest == NULL)
		return -1;

	switch(t->l3protonum) {
	case AF_INET:
		mnl_attr_put_u32(nlh, CTA_IP_V4_SRC, t->src.v4);
		mnl_attr_put_u32(nlh, CTA_IP_V4_DST, t->dst.v4);
		break;
	case AF_INET6:
		mnl_attr_put(nlh, CTA_IP_V6_SRC, sizeof(struct in6_addr),
				&t->src.v6);
		mnl_attr_put(nlh, CTA_IP_V6_DST, sizeof(struct in6_addr),
				&t->dst.v6);
		break;
	default:
		mnl_attr_nest_cancel(nlh, nest);
		return -1;
	}
	mnl_attr_nest_end(nlh, nest);
	return 0;
}
Exemplo n.º 7
0
static int
nfct_build_dnat_port(struct nlmsghdr *nlh, const struct nf_conntrack *ct)
{
	struct nlattr *nest;

	nest = mnl_attr_nest_start(nlh, CTA_NAT_DST);
	nfct_build_protonat(nlh, ct, &ct->dnat);
	mnl_attr_nest_end(nlh, nest);
	return 0;
}
Exemplo n.º 8
0
static int
nfct_build_snat_ipv4(struct nlmsghdr *nlh, const struct nf_conntrack *ct)
{
	struct nlattr *nest;

	nest = mnl_attr_nest_start(nlh, CTA_NAT_SRC);
	nfct_build_nat(nlh, &ct->snat);
	mnl_attr_nest_end(nlh, nest);
	return 0;
}
Exemplo n.º 9
0
static void put_msg(char *buf, uint16_t i, int seq)
{
	struct nlmsghdr *nlh;
	struct nfgenmsg *nfh;
	struct nlattr *nest1, *nest2;

	nlh = mnl_nlmsg_put_header(buf);
	nlh->nlmsg_type = (NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_NEW;
	nlh->nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL|NLM_F_ACK;
	nlh->nlmsg_seq = seq;

	nfh = mnl_nlmsg_put_extra_header(nlh, sizeof(struct nfgenmsg));
	nfh->nfgen_family = AF_INET;
	nfh->version = NFNETLINK_V0;
	nfh->res_id = 0;

	nest1 = mnl_attr_nest_start(nlh, CTA_TUPLE_ORIG);
	nest2 = mnl_attr_nest_start(nlh, CTA_TUPLE_IP);
	mnl_attr_put_u32(nlh, CTA_IP_V4_SRC, inet_addr("1.1.1.1"));
	mnl_attr_put_u32(nlh, CTA_IP_V4_DST, inet_addr("2.2.2.2"));
	mnl_attr_nest_end(nlh, nest2);

	nest2 = mnl_attr_nest_start(nlh, CTA_TUPLE_PROTO);
	mnl_attr_put_u8(nlh, CTA_PROTO_NUM, IPPROTO_TCP);
	mnl_attr_put_u16(nlh, CTA_PROTO_SRC_PORT, htons(i));
	mnl_attr_put_u16(nlh, CTA_PROTO_DST_PORT, htons(1025));
	mnl_attr_nest_end(nlh, nest2);
	mnl_attr_nest_end(nlh, nest1);

	nest1 = mnl_attr_nest_start(nlh, CTA_TUPLE_REPLY);
	nest2 = mnl_attr_nest_start(nlh, CTA_TUPLE_IP);
	mnl_attr_put_u32(nlh, CTA_IP_V4_SRC, inet_addr("2.2.2.2"));
	mnl_attr_put_u32(nlh, CTA_IP_V4_DST, inet_addr("1.1.1.1"));
	mnl_attr_nest_end(nlh, nest2);

	nest2 = mnl_attr_nest_start(nlh, CTA_TUPLE_PROTO);
	mnl_attr_put_u8(nlh, CTA_PROTO_NUM, IPPROTO_TCP);
	mnl_attr_put_u16(nlh, CTA_PROTO_SRC_PORT, htons(1025));
	mnl_attr_put_u16(nlh, CTA_PROTO_DST_PORT, htons(i));
	mnl_attr_nest_end(nlh, nest2);
	mnl_attr_nest_end(nlh, nest1);

	nest1 = mnl_attr_nest_start(nlh, CTA_PROTOINFO);
	nest2 = mnl_attr_nest_start(nlh, CTA_PROTOINFO_TCP);
	mnl_attr_put_u8(nlh, CTA_PROTOINFO_TCP_STATE, TCP_CONNTRACK_SYN_SENT);
	mnl_attr_nest_end(nlh, nest2);
	mnl_attr_nest_end(nlh, nest1);

	mnl_attr_put_u32(nlh, CTA_STATUS, htonl(IPS_CONFIRMED));
	mnl_attr_put_u32(nlh, CTA_TIMEOUT, htonl(1000));
}
Exemplo n.º 10
0
static int
nfct_build_nat_seq_adj(struct nlmsghdr *nlh, const struct nf_conntrack *ct,
		      int dir)
{
	int type = (dir == __DIR_ORIG) ? CTA_NAT_SEQ_ADJ_ORIG :
					 CTA_NAT_SEQ_ADJ_REPLY;
	struct nlattr *nest;

	nest = mnl_attr_nest_start(nlh, type);
	nfct_nat_seq_adj(nlh, ct, dir);
	mnl_attr_nest_end(nlh, nest);

	return 0;
}
Exemplo n.º 11
0
static int
nfct_build_helper_name(struct nlmsghdr *nlh, const struct nf_conntrack *ct)
{
	struct nlattr *nest;

	nest = mnl_attr_nest_start(nlh, CTA_HELP);
	mnl_attr_put_strz(nlh, CTA_HELP_NAME, ct->helper_name);

	if (ct->helper_info != NULL) {
		mnl_attr_put(nlh, CTA_HELP_INFO, ct->helper_info_len,
				ct->helper_info);
	}
	mnl_attr_nest_end(nlh, nest);
	return 0;
}
Exemplo n.º 12
0
static int
nfct_build_protonat(struct nlmsghdr *nlh, const struct nf_conntrack *ct,
		   const struct __nfct_nat *nat)
{
	struct nlattr *nest;

	nest = mnl_attr_nest_start(nlh, CTA_NAT_PROTO);

	switch (ct->head.orig.protonum) {
	case IPPROTO_TCP:
	case IPPROTO_UDP:
		mnl_attr_put_u16(nlh, CTA_PROTONAT_PORT_MIN,
				 nat->l4min.tcp.port);
		mnl_attr_put_u16(nlh, CTA_PROTONAT_PORT_MAX,
				 nat->l4max.tcp.port);
	break;
	}
	mnl_attr_nest_end(nlh, nest);
	return 0;
}
Exemplo n.º 13
0
static int link_mon_peer_list(uint32_t mon_ref)
{
	struct nlmsghdr *nlh;
	char buf[MNL_SOCKET_BUFFER_SIZE];
	struct nlattr *nest;
	int err = 0;

	nlh = msg_init(buf, TIPC_NL_MON_PEER_GET);
	if (!nlh) {
		fprintf(stderr, "error, message initialisation failed\n");
		return -1;
	}

	nest = mnl_attr_nest_start(nlh, TIPC_NLA_MON);
	mnl_attr_put_u32(nlh, TIPC_NLA_MON_REF, mon_ref);
	mnl_attr_nest_end(nlh, nest);

	err = msg_dumpit(nlh, link_mon_peer_list_cb, NULL);
	return err;
}
Exemplo n.º 14
0
int
nfct_build_tuple(struct nlmsghdr *nlh, const struct __nfct_tuple *t, int type)
{
	struct nlattr *nest;

	nest = mnl_attr_nest_start(nlh, type);
	if (nest == NULL)
		return -1;

	if (nfct_build_tuple_ip(nlh, t) < 0)
		goto err;
	if (nfct_build_tuple_proto(nlh, t) < 0)
		goto err;

	mnl_attr_nest_end(nlh, nest);
	return 0;
err:
	mnl_attr_nest_cancel(nlh, nest);
	return -1;
}
Exemplo n.º 15
0
static int cmd_link_stat_reset(struct nlmsghdr *nlh, const struct cmd *cmd,
			       struct cmdl *cmdl, void *data)
{
	char *link;
	char buf[MNL_SOCKET_BUFFER_SIZE];
	struct opt *opt;
	struct nlattr *nest;
	struct opt opts[] = {
		{ "link",		OPT_KEYVAL,	NULL },
		{ NULL }
	};

	if (help_flag) {
		(cmd->help)(cmdl);
		return -EINVAL;
	}

	if (parse_opts(opts, cmdl) != 1) {
		(cmd->help)(cmdl);
		return -EINVAL;
	}

	nlh = msg_init(buf, TIPC_NL_LINK_RESET_STATS);
	if (!nlh) {
		fprintf(stderr, "error, message initialisation failed\n");
		return -1;
	}

	opt = get_opt(opts, "link");
	if (!opt) {
		fprintf(stderr, "error, missing link\n");
		return -EINVAL;
	}
	link = opt->val;

	nest = mnl_attr_nest_start(nlh, TIPC_NLA_LINK);
	mnl_attr_put_strz(nlh, TIPC_NLA_LINK_NAME, link);
	mnl_attr_nest_end(nlh, nest);

	return msg_doit(nlh, NULL, NULL);
}
Exemplo n.º 16
0
static int
nfct_build_tuple_proto(struct nlmsghdr *nlh, const struct __nfct_tuple *t)
{
	struct nlattr *nest;

	nest = mnl_attr_nest_start(nlh, CTA_TUPLE_PROTO);
	if (nest == NULL)
		return -1;

	mnl_attr_put_u8(nlh, CTA_PROTO_NUM, t->protonum);

	switch(t->protonum) {
	case IPPROTO_UDP:
	case IPPROTO_TCP:
	case IPPROTO_SCTP:
	case IPPROTO_DCCP:
	case IPPROTO_GRE:
	case IPPROTO_UDPLITE:
		mnl_attr_put_u16(nlh, CTA_PROTO_SRC_PORT, t->l4src.tcp.port);
		mnl_attr_put_u16(nlh, CTA_PROTO_DST_PORT, t->l4dst.tcp.port);
		break;
	case IPPROTO_ICMP:
		mnl_attr_put_u8(nlh, CTA_PROTO_ICMP_CODE, t->l4dst.icmp.code);
		mnl_attr_put_u8(nlh, CTA_PROTO_ICMP_TYPE, t->l4dst.icmp.type);
		mnl_attr_put_u16(nlh, CTA_PROTO_ICMP_ID, t->l4src.icmp.id);
		break;
	case IPPROTO_ICMPV6:
		mnl_attr_put_u8(nlh, CTA_PROTO_ICMPV6_CODE, t->l4dst.icmp.code);
		mnl_attr_put_u8(nlh, CTA_PROTO_ICMPV6_TYPE, t->l4dst.icmp.type);
		mnl_attr_put_u16(nlh, CTA_PROTO_ICMPV6_ID, t->l4src.icmp.id);
		break;
	default:
		mnl_attr_nest_cancel(nlh, nest);
		return -1;
	}
	mnl_attr_nest_end(nlh, nest);
	return 0;
}
Exemplo n.º 17
0
static int
nfct_build_protoinfo(struct nlmsghdr *nlh, const struct nf_conntrack *ct)
{
	struct nlattr *nest, *nest_proto;

	switch(ct->head.orig.protonum) {
	case IPPROTO_TCP:
		/* Preliminary attribute check to avoid sending an empty
		 * CTA_PROTOINFO_TCP nest, which results in EINVAL in
		 * Linux kernel <= 2.6.25. */
		if (!(test_bit(ATTR_TCP_STATE, ct->head.set) ||
		      test_bit(ATTR_TCP_FLAGS_ORIG, ct->head.set) ||
		      test_bit(ATTR_TCP_FLAGS_REPL, ct->head.set) ||
		      test_bit(ATTR_TCP_MASK_ORIG, ct->head.set) ||
		      test_bit(ATTR_TCP_MASK_REPL, ct->head.set) ||
		      test_bit(ATTR_TCP_WSCALE_ORIG, ct->head.set) ||
		      test_bit(ATTR_TCP_WSCALE_REPL, ct->head.set))) {
			break;
		}
		nest = mnl_attr_nest_start(nlh, CTA_PROTOINFO);
		nest_proto = mnl_attr_nest_start(nlh, CTA_PROTOINFO_TCP);
		if (test_bit(ATTR_TCP_STATE, ct->head.set)) {
			mnl_attr_put_u8(nlh, CTA_PROTOINFO_TCP_STATE,
					ct->protoinfo.tcp.state);
		}
		if (test_bit(ATTR_TCP_FLAGS_ORIG, ct->head.set) &&
		    test_bit(ATTR_TCP_MASK_ORIG, ct->head.set)) {
			mnl_attr_put(nlh, CTA_PROTOINFO_TCP_FLAGS_ORIGINAL,
				     sizeof(struct nf_ct_tcp_flags),
				     &ct->protoinfo.tcp.flags[0]);
		}
		if (test_bit(ATTR_TCP_FLAGS_REPL, ct->head.set) &&
		    test_bit(ATTR_TCP_MASK_REPL, ct->head.set)) {
			mnl_attr_put(nlh, CTA_PROTOINFO_TCP_FLAGS_REPLY,
				     sizeof(struct nf_ct_tcp_flags),
				     &ct->protoinfo.tcp.flags[1]);
		}
		if (test_bit(ATTR_TCP_WSCALE_ORIG, ct->head.set)) {
			mnl_attr_put_u8(nlh, CTA_PROTOINFO_TCP_WSCALE_ORIGINAL,
					ct->protoinfo.tcp.wscale[__DIR_ORIG]);
		}
		if (test_bit(ATTR_TCP_WSCALE_REPL, ct->head.set)) {
			mnl_attr_put_u8(nlh, CTA_PROTOINFO_TCP_WSCALE_REPLY,
					ct->protoinfo.tcp.wscale[__DIR_REPL]);
		}
		mnl_attr_nest_end(nlh, nest_proto);
		mnl_attr_nest_end(nlh, nest);
		break;
	case IPPROTO_SCTP:
		/* See comment above on TCP. */
		if (!(test_bit(ATTR_SCTP_STATE, ct->head.set) ||
		      test_bit(ATTR_SCTP_VTAG_ORIG, ct->head.set) ||
		      test_bit(ATTR_SCTP_VTAG_REPL, ct->head.set))) {
			break;
		}
		nest = mnl_attr_nest_start(nlh, CTA_PROTOINFO);
		nest_proto = mnl_attr_nest_start(nlh, CTA_PROTOINFO_SCTP);

		if (test_bit(ATTR_SCTP_STATE, ct->head.set)) {
			mnl_attr_put_u8(nlh, CTA_PROTOINFO_SCTP_STATE,
					ct->protoinfo.sctp.state);
		}
		if (test_bit(ATTR_SCTP_VTAG_ORIG, ct->head.set)) {
			mnl_attr_put_u32(nlh, CTA_PROTOINFO_SCTP_VTAG_ORIGINAL,
				htonl(ct->protoinfo.sctp.vtag[__DIR_ORIG]));
		}
		if (test_bit(ATTR_SCTP_VTAG_REPL, ct->head.set)) {
			mnl_attr_put_u32(nlh, CTA_PROTOINFO_SCTP_VTAG_REPLY,
				htonl(ct->protoinfo.sctp.vtag[__DIR_REPL]));
		}
		mnl_attr_nest_end(nlh, nest_proto);
		mnl_attr_nest_end(nlh, nest);
		break;
	case IPPROTO_DCCP:
		/* See comment above on TCP. */
		if (!(test_bit(ATTR_DCCP_STATE, ct->head.set) ||
		      test_bit(ATTR_DCCP_ROLE, ct->head.set) ||
		      test_bit(ATTR_DCCP_HANDSHAKE_SEQ, ct->head.set))) {
			break;
		}
		nest = mnl_attr_nest_start(nlh, CTA_PROTOINFO);
		nest_proto = mnl_attr_nest_start(nlh, CTA_PROTOINFO_DCCP);
		if (test_bit(ATTR_DCCP_STATE, ct->head.set)) {
			mnl_attr_put_u8(nlh, CTA_PROTOINFO_DCCP_STATE,
					ct->protoinfo.dccp.state);
		}
		if (test_bit(ATTR_DCCP_ROLE, ct->head.set)) {
			mnl_attr_put_u8(nlh, CTA_PROTOINFO_DCCP_ROLE,
					ct->protoinfo.dccp.role);
		}
		if (test_bit(ATTR_DCCP_HANDSHAKE_SEQ, ct->head.set)) {
			uint64_t handshake_seq =
				be64toh(ct->protoinfo.dccp.handshake_seq);

			mnl_attr_put_u64(nlh, CTA_PROTOINFO_DCCP_HANDSHAKE_SEQ,
					 handshake_seq);
		}
		mnl_attr_nest_end(nlh, nest_proto);
		mnl_attr_nest_end(nlh, nest);
	default:
		break;
	}
	return 0;
}
int main(int argc, char *argv[]){
    struct mnl_socket *nl_sock = NULL;
    struct nlmsghdr *nlh = NULL;
    struct nlmsgerr *nlerr = NULL;
    struct nlattr *linkinfo = NULL, *tunnelinfo = NULL;
    struct ifinfomsg *ifinfo = NULL;
    uint8_t buf[MNL_SOCKET_BUFFER_SIZE];
    size_t numbytes = 0;
    struct in_addr addr;

    memset(buf, 0, sizeof(buf));

    if((nl_sock = mnl_socket_open(NETLINK_ROUTE)) == NULL){
        perror("mnl_socket_open: ");
        exit(EXIT_FAILURE);
    }

    //Add and configure header
    nlh = mnl_nlmsg_put_header(buf);
    //Create only if interface does not exists from before (EXCL)
    nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK;
    nlh->nlmsg_type = RTM_NEWLINK; 

    //see rtnl_newlink in net/core/rtnetlink.c for observing how this function
    //works
    ifinfo = mnl_nlmsg_put_extra_header(nlh, sizeof(struct ifinfomsg));
    ifinfo->ifi_family = AF_UNSPEC;

    //Add a name and mtu, for testing
    mnl_attr_put_str(nlh, IFLA_IFNAME, "kre2");
    mnl_attr_put_u32(nlh, IFLA_MTU, 1234);
  
    //Type is required, set to IPIP
    linkinfo = mnl_attr_nest_start(nlh, IFLA_LINKINFO);
    mnl_attr_put_str(nlh, IFLA_INFO_KIND, "ipip");

    //Add information about the tunnel
    tunnelinfo = mnl_attr_nest_start(nlh, IFLA_INFO_DATA);
    inet_pton(AF_INET, "192.168.203.19", &addr);
    mnl_attr_put_u32(nlh, IFLA_IPTUN_LOCAL, addr.s_addr);
    inet_pton(AF_INET, "10.0.0.2", &addr);
    mnl_attr_put_u32(nlh, IFLA_IPTUN_REMOTE, addr.s_addr);

    mnl_attr_nest_end(nlh, tunnelinfo);
    mnl_attr_nest_end(nlh, linkinfo);

    numbytes = mnl_socket_sendto(nl_sock, nlh, nlh->nlmsg_len);
    numbytes = mnl_socket_recvfrom(nl_sock, buf, sizeof(buf));

    nlh = (struct nlmsghdr*) buf;

    if(nlh->nlmsg_type == NLMSG_ERROR){
        nlerr = mnl_nlmsg_get_payload(nlh);
        //error==0 for ack
        if(nlerr->error)
            printf("Error: %s\n", strerror(-nlerr->error));
    } else {
        printf("Link added\n");
    }

    return EXIT_FAILURE;
}