static int qdisc_request_update(struct nl_cache *c, struct nl_sock *sk) { struct tcmsg tchdr = { .tcm_family = AF_UNSPEC, .tcm_ifindex = c->c_iarg1, }; return nl_send_simple(sk, RTM_GETQDISC, NLM_F_DUMP, &tchdr, sizeof(tchdr)); } /** * @name Allocation/Freeing * @{ */ struct rtnl_qdisc *rtnl_qdisc_alloc(void) { struct rtnl_tc *tc; tc = TC_CAST(nl_object_alloc(&qdisc_obj_ops)); if (tc) tc->tc_type = RTNL_TC_TYPE_QDISC; return (struct rtnl_qdisc *) tc; } void rtnl_qdisc_put(struct rtnl_qdisc *qdisc) { nl_object_put((struct nl_object *) qdisc); }
static int qdisc_request_update(struct nl_cache *c, struct nl_sock *sk) { struct tcmsg tchdr = { .tcm_family = AF_UNSPEC, .tcm_ifindex = c->c_iarg1, }; return nl_send_simple(sk, RTM_GETQDISC, NLM_F_DUMP, &tchdr, sizeof(tchdr)); } /** * @name QDisc Addition * @{ */ static int qdisc_build(struct rtnl_qdisc *qdisc, int type, int flags, struct nl_msg **result) { return rtnl_tc_msg_build(TC_CAST(qdisc), type, flags, result); #if 0 /* Some qdiscs don't accept properly nested messages (e.g. netem). To * accomodate for this, they can complete the message themselves. */ else if (qops && qops->qo_build_msg) { err = qops->qo_build_msg(qdisc, *result); if (err < 0) goto errout; } #endif }
int netlink_request(void *request, __u16 request_len, int (*cb)(struct nl_msg *, void *), void *cb_arg) { struct nl_sock *sk; enum nl_cb_type callbacks[] = { NL_CB_VALID, NL_CB_FINISH, NL_CB_ACK }; int i; int error; sk = nl_socket_alloc(); if (!sk) { log_err(ERR_ALLOC_FAILED, "Could not allocate a socket; cannot speak to the NAT64."); return -ENOMEM; } for (i = 0; i < (sizeof(callbacks) / sizeof(callbacks[0])); i++) { error = nl_socket_modify_cb(sk, callbacks[i], NL_CB_CUSTOM, cb, cb_arg); if (error < 0) { log_err(ERR_NETLINK, "Could not register response handler. " "I won't be able to parse the NAT64's response, so I won't send the request.\n" "Netlink error message: %s (Code %d)", nl_geterror(error), error); goto fail_free; } } error = nl_connect(sk, NETLINK_USERSOCK); if (error < 0) { log_err(ERR_NETLINK, "Could not bind the socket to the NAT64.\n" "Netlink error message: %s (Code %d)", nl_geterror(error), error); goto fail_free; } error = nl_send_simple(sk, MSG_TYPE_NAT64, 0, request, request_len); if (error < 0) { log_err(ERR_NETLINK, "Could not send the request to the NAT64 (is it really up?).\n" "Netlink error message: %s (Code %d)", nl_geterror(error), error); goto fail_close; } error = nl_recvmsgs_default(sk); if (error < 0) { log_err(ERR_NETLINK, "%s (System error %d)", nl_geterror(error), error); goto fail_close; } nl_close(sk); nl_socket_free(sk); return 0; fail_close: nl_close(sk); /* Fall through. */ fail_free: nl_socket_free(sk); return -EINVAL; }
/** * Send a Generic Netlink message consisting only of a header * @arg sk Generic Netlink socket * @arg family Numeric family identifier * @arg cmd Numeric command identifier * @arg version Interface version * @arg flags Additional Netlink message flags (optional) * * This function is a shortcut for sending a Generic Netlink message without * any message payload. The message will only consist of the Netlink and * Generic Netlink headers. The header is constructed based on the specified * parameters and passed on to nl_send_simple() to send it on the specified * socket. * * @par Example: * @code * #include <netlink/genl/genl.h> * #include <linux/genetlink.h> * * err = genl_send_simple(sk, GENL_ID_CTRL, CTRL_CMD_GETFAMILY, CTRL_VERSION, * NLM_F_DUMP); * @endcode * * @see nl_send_simple() * * @return 0 on success or a negative error code. Due to a bug, this function * returns the number of bytes sent. Treat any non-negative number as success. */ int genl_send_simple(struct nl_sock *sk, int family, int cmd, int version, int flags) { struct genlmsghdr hdr = { .cmd = cmd, .version = version, }; return nl_send_simple(sk, family, flags, &hdr, sizeof(hdr)); }
/** * Send routing netlink request message * @arg sk Netlink socket. * @arg type Netlink message type. * @arg family Address family. * @arg flags Additional netlink message flags. * * Fills out a routing netlink request message and sends it out * using nl_send_simple(). * * @return 0 on success or a negative error code. Due to a bug in * older versions, this returned the number of bytes sent. So for * compatibility, treat positive return values as success too. */ int nl_rtgen_request(struct nl_sock *sk, int type, int family, int flags) { int err; struct rtgenmsg gmsg = { .rtgen_family = family, }; err = nl_send_simple(sk, type, flags, &gmsg, sizeof(gmsg)); return err >= 0 ? 0 : err; }
/** * Send trivial netfilter netlink message * @arg sk Netlink socket. * @arg subsys_id nfnetlink subsystem * @arg type nfnetlink message type * @arg flags message flags * @arg family nfnetlink address family * @arg res_id nfnetlink resource id * * @return 0 on success or a negative error code. Due to a bug, this function * returns the number of bytes sent. Treat any non-negative number as success. */ int nfnl_send_simple(struct nl_sock *sk, uint8_t subsys_id, uint8_t type, int flags, uint8_t family, uint16_t res_id) { struct nfgenmsg hdr = { .nfgen_family = family, .version = NFNETLINK_V0, .res_id = htons(res_id), }; return nl_send_simple(sk, NFNLMSG_TYPE(subsys_id, type), flags, &hdr, sizeof(hdr)); }
/** * Send trivial idiag netlink message * @arg sk Netlink socket. * @arg flags Message flags * @arg family Address family * @arg states Socket states to query * @arg ext Inet Diag attribute extensions to query * * @return Newly allocated netlink message or NULL. */ int idiagnl_send_simple(struct nl_sock *sk, int flags, uint8_t family, uint16_t states, uint16_t ext) { struct inet_diag_req req; memset(&req, 0, sizeof(req)); flags |= NLM_F_ROOT; req.idiag_family = family; req.idiag_states = states; req.idiag_ext = ext; return nl_send_simple(sk, TCPDIAG_GETSOCK, flags, &req, sizeof(req)); }
static int cls_request_update(struct nl_cache *cache, struct nl_sock *sk) { struct tcmsg tchdr = { .tcm_family = AF_UNSPEC, .tcm_ifindex = cache->c_iarg1, .tcm_parent = cache->c_iarg2, }; return nl_send_simple(sk, RTM_GETTFILTER, NLM_F_DUMP, &tchdr, sizeof(tchdr)); } static struct rtnl_tc_type_ops cls_ops = { .tt_type = RTNL_TC_TYPE_CLS, .tt_dump_prefix = "cls", .tt_dump = { [NL_DUMP_LINE] = cls_dump_line, }, };
int main (int argc, char **argv) { struct nl_sock *sk; int err; sk = setup_socket (); g_assert (sk); err = nl_send_simple (sk, XFRM_MSG_GETPOLICY, NLM_F_DUMP, NULL, 0); if (err < 0) { g_warning ("Error sending: %d %s", err, nl_geterror (err)); return 1; } nl_socket_modify_cb (sk, NL_CB_VALID, NL_CB_CUSTOM, parse_reply, NULL); err = nl_recvmsgs_default (sk); if (err < 0) { g_warning ("Error parsing: %d %s", err, nl_geterror (err)); return 1; } return 0; }
static int route_request_update(struct nl_cache *c, struct nl_sock *h) { struct rtmsg rhdr = { .rtm_family = c->c_iarg1, }; if (c->c_iarg2 & ROUTE_CACHE_CONTENT) rhdr.rtm_flags |= RTM_F_CLONED; return nl_send_simple(h, RTM_GETROUTE, NLM_F_DUMP, &rhdr, sizeof(rhdr)); } /** * @name Cache Management * @{ */ /** * Build a route cache holding all routes currently configured in the kernel * @arg sk Netlink socket. * @arg family Address family of routes to cover or AF_UNSPEC * @arg flags Flags * * Allocates a new cache, initializes it properly and updates it to * contain all routes currently configured in the kernel. * * @note The caller is responsible for destroying and freeing the * cache after using it. * @return The cache or NULL if an error has occured. */ int rtnl_route_alloc_cache(struct nl_sock *sk, int family, int flags, struct nl_cache **result) { struct nl_cache *cache; int err; if (!(cache = nl_cache_alloc(&rtnl_route_ops))) return -NLE_NOMEM; cache->c_iarg1 = family; cache->c_iarg2 = flags; if (sk && (err = nl_cache_refill(sk, cache)) < 0) { free(cache); return err; } *result = cache; return 0; } /** @} */ /** * @name Route Addition * @{ */ static int build_route_msg(struct rtnl_route *tmpl, int cmd, int flags, struct nl_msg **result) { struct nl_msg *msg; int err; if (!(msg = nlmsg_alloc_simple(cmd, flags))) return -NLE_NOMEM; if ((err = rtnl_route_build_msg(msg, tmpl)) < 0) { nlmsg_free(msg); return err; } *result = msg; return 0; } int rtnl_route_build_add_request(struct rtnl_route *tmpl, int flags, struct nl_msg **result) { return build_route_msg(tmpl, RTM_NEWROUTE, NLM_F_CREATE | flags, result); } int rtnl_route_add(struct nl_sock *sk, struct rtnl_route *route, int flags) { struct nl_msg *msg; int err; if ((err = rtnl_route_build_add_request(route, flags, &msg)) < 0) return err; err = nl_send_auto_complete(sk, msg); nlmsg_free(msg); if (err < 0) return err; return wait_for_ack(sk); } int rtnl_route_build_del_request(struct rtnl_route *tmpl, int flags, struct nl_msg **result) { return build_route_msg(tmpl, RTM_DELROUTE, flags, result); } int rtnl_route_delete(struct nl_sock *sk, struct rtnl_route *route, int flags) { struct nl_msg *msg; int err; if ((err = rtnl_route_build_del_request(route, flags, &msg)) < 0) return err; err = nl_send_auto_complete(sk, msg); nlmsg_free(msg); if (err < 0) return err; return wait_for_ack(sk); } /** @} */ static struct nl_af_group route_groups[] = { { AF_INET, RTNLGRP_IPV4_ROUTE }, { AF_INET6, RTNLGRP_IPV6_ROUTE }, { AF_DECnet, RTNLGRP_DECnet_ROUTE }, { END_OF_GROUP_LIST }, }; static struct nl_cache_ops rtnl_route_ops = { .co_name = "route/route", .co_hdrsize = sizeof(struct rtmsg), .co_msgtypes = { { RTM_NEWROUTE, NL_ACT_NEW, "new" }, { RTM_DELROUTE, NL_ACT_DEL, "del" }, { RTM_GETROUTE, NL_ACT_GET, "get" }, END_OF_MSGTYPES_LIST, }, .co_protocol = NETLINK_ROUTE, .co_groups = route_groups, .co_request_update = route_request_update, .co_msg_parser = route_msg_parser, .co_obj_ops = &route_obj_ops, }; static void __init route_init(void) { nl_cache_mngt_register(&rtnl_route_ops); } static void __exit route_exit(void) { nl_cache_mngt_unregister(&rtnl_route_ops); }
static int cls_request_update(struct nl_cache *cache, struct nl_sock *sk) { struct tcmsg tchdr = { .tcm_family = AF_UNSPEC, .tcm_ifindex = cache->c_iarg1, .tcm_parent = cache->c_iarg2, }; return nl_send_simple(sk, RTM_GETTFILTER, NLM_F_DUMP, &tchdr, sizeof(tchdr)); } static int cls_build(struct rtnl_cls *cls, int type, int flags, struct nl_msg **result) { struct rtnl_cls_ops *cops; int err, prio, proto; struct tcmsg *tchdr; err = tca_build_msg((struct rtnl_tca *) cls, type, flags, result); if (err < 0) return err; tchdr = nlmsg_data(nlmsg_hdr(*result)); prio = rtnl_cls_get_prio(cls); proto = rtnl_cls_get_protocol(cls); tchdr->tcm_info = TC_H_MAKE(prio << 16, htons(proto)); cops = rtnl_cls_lookup_ops(cls); if (cops && cops->co_get_opts) { struct nl_msg *opts; if (!(opts = nlmsg_alloc())) { err = -NLE_NOMEM; goto errout; } if (!(err = cops->co_get_opts(cls, opts))) err = nla_put_nested(*result, TCA_OPTIONS, opts); nlmsg_free(opts); if (err < 0) goto errout; } return 0; errout: nlmsg_free(*result); return err; } /** * @name Classifier Addition/Modification/Deletion * @{ */ /** * Build a netlink message to add a new classifier * @arg cls classifier to add * @arg flags additional netlink message flags * @arg result Pointer to store resulting message. * * Builds a new netlink message requesting an addition of a classifier * The netlink message header isn't fully equipped with all relevant * fields and must be sent out via nl_send_auto_complete() or * supplemented as needed. \a classifier must contain the attributes of * the new classifier set via \c rtnl_cls_set_* functions. \a opts * may point to the clsasifier specific options. * * @return 0 on success or a negative error code. */ int rtnl_cls_build_add_request(struct rtnl_cls *cls, int flags, struct nl_msg **result) { return cls_build(cls, RTM_NEWTFILTER, NLM_F_CREATE | flags, result); } /** * Add a new classifier * @arg sk Netlink socket. * @arg cls classifier to add * @arg flags additional netlink message flags * * Builds a netlink message by calling rtnl_cls_build_add_request(), * sends the request to the kernel and waits for the next ACK to be * received and thus blocks until the request has been processed. * * @return 0 on sucess or a negative error if an error occured. */ int rtnl_cls_add(struct nl_sock *sk, struct rtnl_cls *cls, int flags) { struct nl_msg *msg; int err; if ((err = rtnl_cls_build_add_request(cls, flags, &msg)) < 0) return err; err = nl_send_auto_complete(sk, msg); nlmsg_free(msg); if (err < 0) return err; return nl_wait_for_ack(sk); } /** * Build a netlink message to change classifier attributes * @arg cls classifier to change * @arg flags additional netlink message flags * @arg result Pointer to store resulting message. * * Builds a new netlink message requesting a change of a neigh * attributes. The netlink message header isn't fully equipped with * all relevant fields and must thus be sent out via nl_send_auto_complete() * or supplemented as needed. * * @return 0 on success or a negative error code. */ int rtnl_cls_build_change_request(struct rtnl_cls *cls, int flags, struct nl_msg **result) { return cls_build(cls, RTM_NEWTFILTER, NLM_F_REPLACE | flags, result); } /** * Change a classifier * @arg sk Netlink socket. * @arg cls classifier to change * @arg flags additional netlink message flags * * Builds a netlink message by calling rtnl_cls_build_change_request(), * sends the request to the kernel and waits for the next ACK to be * received and thus blocks until the request has been processed. * * @return 0 on sucess or a negative error if an error occured. */ int rtnl_cls_change(struct nl_sock *sk, struct rtnl_cls *cls, int flags) { struct nl_msg *msg; int err; if ((err = rtnl_cls_build_change_request(cls, flags, &msg)) < 0) return err; err = nl_send_auto_complete(sk, msg); nlmsg_free(msg); if (err < 0) return err; return nl_wait_for_ack(sk); } /** * Build a netlink request message to delete a classifier * @arg cls classifier to delete * @arg flags additional netlink message flags * @arg result Pointer to store resulting message. * * Builds a new netlink message requesting a deletion of a classifier. * The netlink message header isn't fully equipped with all relevant * fields and must thus be sent out via nl_send_auto_complete() * or supplemented as needed. * * @return 0 on success or a negative error code. */ int rtnl_cls_build_delete_request(struct rtnl_cls *cls, int flags, struct nl_msg **result) { return cls_build(cls, RTM_DELTFILTER, flags, result); } /** * Delete a classifier * @arg sk Netlink socket. * @arg cls classifier to delete * @arg flags additional netlink message flags * * Builds a netlink message by calling rtnl_cls_build_delete_request(), * sends the request to the kernel and waits for the next ACK to be * received and thus blocks until the request has been processed. * * @return 0 on sucess or a negative error if an error occured. */ int rtnl_cls_delete(struct nl_sock *sk, struct rtnl_cls *cls, int flags) { struct nl_msg *msg; int err; if ((err = rtnl_cls_build_delete_request(cls, flags, &msg)) < 0) return err; err = nl_send_auto_complete(sk, msg); nlmsg_free(msg); if (err < 0) return err; return nl_wait_for_ack(sk); } /** @} */ /** * @name Cache Management * @{ */ /** * Build a classifier cache including all classifiers attached to the * specified class/qdisc on eht specified interface. * @arg sk Netlink socket. * @arg ifindex interface index of the link the classes are * attached to. * @arg parent parent qdisc/class * @arg result Pointer to store resulting cache. * * Allocates a new cache, initializes it properly and updates it to * include all classes attached to the specified interface. * * @note The caller is responsible for destroying and freeing the * cache after using it. * @return 0 on success or a negative error code. */ int rtnl_cls_alloc_cache(struct nl_sock *sk, int ifindex, uint32_t parent, struct nl_cache **result) { struct nl_cache * cache; int err; if (!(cache = nl_cache_alloc(&rtnl_cls_ops))) return -NLE_NOMEM; cache->c_iarg1 = ifindex; cache->c_iarg2 = parent; if (sk && (err = nl_cache_refill(sk, cache)) < 0) { nl_cache_free(cache); return err; } *result = cache; return 0; } /** @} */ static struct nl_cache_ops rtnl_cls_ops = { .co_name = "route/cls", .co_hdrsize = sizeof(struct tcmsg), .co_msgtypes = { { RTM_NEWTFILTER, NL_ACT_NEW, "new" }, { RTM_DELTFILTER, NL_ACT_DEL, "del" }, { RTM_GETTFILTER, NL_ACT_GET, "get" }, END_OF_MSGTYPES_LIST, }, .co_protocol = NETLINK_ROUTE, .co_request_update = cls_request_update, .co_msg_parser = cls_msg_parser, .co_obj_ops = &cls_obj_ops, }; static void __init cls_init(void) { nl_cache_mngt_register(&rtnl_cls_ops); } static void __exit cls_exit(void) { nl_cache_mngt_unregister(&rtnl_cls_ops); }
int main(int argc, char *argv[]) { struct stat sb; // To check if config file exist. struct in_addr iaddrf, iaddrl; // To validate IP addresses struct in6_addr i6addrf; // also cfg_t *cfg, *cfg_ipv4, *cfg_ipv6; const char *sect_name; char *addr_first, *addr_last; unsigned char addr_maskbits; int port_first, port_last; char which[sizeof("ABC")]; struct config_struct cs; struct nl_sock *nls; int ret; if (argc != 2) { printf("Usage: %s <config-file>\n", argv[0]); exit(EXIT_FAILURE); } if ( stat(argv[1], &sb) == -1 ) { printf("Error: Can not open configuration file: %s\n", argv[1]); exit(EXIT_FAILURE); } cfg_opt_t ipv4_opts[] = { CFG_STR("ipv4_addr_net", IPV4_DEF_NET, CFGF_NONE), CFG_INT("ipv4_addr_net_mask_bits", IPV4_DEF_MASKBITS, CFGF_NONE), CFG_STR("ipv4_pool_range_first", IPV4_DEF_POOL_FIRST, CFGF_NONE), CFG_STR("ipv4_pool_range_last", IPV4_DEF_POOL_LAST, CFGF_NONE), CFG_INT("ipv4_tcp_port_range_first", IPV4_DEF_TCP_POOL_FIRST, CFGF_NONE), CFG_INT("ipv4_tcp_port_range_last", IPV4_DEF_TCP_POOL_LAST, CFGF_NONE), CFG_INT("ipv4_udp_port_range_first", IPV4_DEF_UDP_POOL_FIRST, CFGF_NONE), CFG_INT("ipv4_udp_port_range_last", IPV4_DEF_UDP_POOL_LAST, CFGF_NONE), CFG_END() }; cfg_opt_t ipv6_opts[] = { CFG_STR("ipv6_net_prefix", IPV6_DEF_PREFIX, CFGF_NONE), CFG_INT("ipv6_net_mask_bits", IPV6_DEF_MASKBITS, CFGF_NONE), CFG_INT("ipv6_tcp_port_range_first", IPV6_DEF_TCP_POOL_FIRST, CFGF_NONE), CFG_INT("ipv6_tcp_port_range_last", IPV6_DEF_TCP_POOL_LAST, CFGF_NONE), CFG_INT("ipv6_udp_port_range_first", IPV6_DEF_UDP_POOL_FIRST, CFGF_NONE), CFG_INT("ipv6_udp_port_range_last", IPV6_DEF_UDP_POOL_LAST, CFGF_NONE), CFG_END() }; cfg_opt_t opts[] = { CFG_SEC("ipv4", ipv4_opts, CFGF_NONE), CFG_SEC("ipv6", ipv6_opts, CFGF_NONE), CFG_END() }; cfg = cfg_init(opts, CFGF_NONE); if(cfg_parse(cfg, argv[1]) == CFG_PARSE_ERROR) { printf("Error parsing configuration file: %s\n", argv[1]); exit_error_conf(cfg); } /* Loading IPv4 configuration */ { cfg_ipv4 = cfg_getsec(cfg, "ipv4"); sect_name = cfg_name(cfg_ipv4); printf ("Section: %s\n", sect_name); addr_first = cfg_getstr(cfg_ipv4, "ipv4_addr_net"); if ( inet_aton(addr_first, &iaddrf) == 0 ) // Validate ipv4 addr { printf("Error: Invalid IPv4 address net: %s\n", addr_first); exit_error_conf(cfg); } addr_maskbits = cfg_getint(cfg_ipv4, "ipv4_addr_net_mask_bits"); if (addr_maskbits > 32 || addr_maskbits < 0) { printf("Error: Bad IPv4 network mask bits value: %d\n", addr_maskbits); exit_error_conf(cfg); } cs.ipv4_addr_net = iaddrf; cs.ipv4_addr_net_mask_bits = addr_maskbits; printf("\tPool Network: %s/%d\n", addr_first, addr_maskbits); // addr_first = cfg_getstr(cfg_ipv4, "ipv4_pool_range_first"); addr_last = cfg_getstr(cfg_ipv4, "ipv4_pool_range_last"); if ( inet_aton(addr_first, &iaddrf) == 0 ) // Validate ipv4 addr { printf("Error: Malformed ipv4_pool_range_first: %s\n", addr_first); exit_error_conf(cfg); } if ( inet_aton(addr_last, &iaddrl) == 0 ) // Validate ipv4 addr { printf("Error: Malformed ipv4_pool_range_last: %s\n", addr_last); exit_error_conf(cfg); } if (iaddrf.s_addr > iaddrl.s_addr) // Validate that: first < last { printf("Error: First pool address is greater than last pool address.\n"); exit_error_conf(cfg); } cs.ipv4_pool_range_first = iaddrf; cs.ipv4_pool_range_last = iaddrl; printf("\t\t- First address: %s\n", inet_ntoa(iaddrf)); printf("\t\t- Last address: %s\n", inet_ntoa(iaddrl)); // port_first = cfg_getint(cfg_ipv4, "ipv4_tcp_port_range_first"); port_last = cfg_getint(cfg_ipv4, "ipv4_tcp_port_range_last"); sprintf(which, "TCP"); if (port_first < 0 || port_first > 65535) { printf("Error: Invalid first %s port: %d\n", which, port_first); exit_error_conf(cfg); } if (port_last < 0 || port_last > 65535) { printf("Error: Invalid last %s port: %d\n", which, port_last); exit_error_conf(cfg); } if (port_first > port_last) { printf("Error: First %s port is greater than last port.\n", which); exit_error_conf(cfg); } cs.ipv4_tcp_port_first = port_first; cs.ipv4_tcp_port_last = port_last; printf("\t%s pool port range: %d-%d\n", which, port_first, port_last); // port_first = cfg_getint(cfg_ipv4, "ipv4_udp_port_range_first"); port_last = cfg_getint(cfg_ipv4, "ipv4_udp_port_range_last"); sprintf(which, "UDP"); if (port_first < 0 || port_first > 65535) { printf("Error: Invalid first %s port: %d\n", which, port_first); exit_error_conf(cfg); } if (port_last < 0 || port_last > 65535) { printf("Error: Invalid last %s port: %d\n", which, port_last); exit_error_conf(cfg); } if (port_first > port_last) { printf("Error: First %s port is greater than last port.\n", which); exit_error_conf(cfg); } cs.ipv4_udp_port_first = port_first; cs.ipv4_udp_port_last = port_last; printf("\t%s pool port range: %d-%d\n", which, port_first, port_last); printf ("\n" ); } /* Loading IPv6 configuration */ { cfg_ipv6 = cfg_getsec(cfg, "ipv6"); sect_name = cfg_name(cfg_ipv6); printf ("Section: %s\n", sect_name ); addr_first = cfg_getstr(cfg_ipv6, "ipv6_net_prefix"); if ( inet_pton(AF_INET6, addr_first, &i6addrf) < 1 ) // Validate ipv6 addr { printf("Error: Invalid IPv6 address net: %s\n", addr_first); exit_error_conf(cfg); } addr_maskbits = cfg_getint(cfg_ipv6, "ipv6_net_mask_bits"); if (addr_maskbits > 128 || addr_maskbits < 0) { printf("Error: Bad IPv6 network mask bits value: %d\n", addr_maskbits); exit_error_conf(cfg); } cs.ipv6_net_prefix = i6addrf; cs.ipv6_net_mask_bits = addr_maskbits; printf("\tPrefix: %s/%d\n", addr_first, addr_maskbits); // port_first = cfg_getint(cfg_ipv6, "ipv6_tcp_port_range_first"); port_last = cfg_getint(cfg_ipv6, "ipv6_tcp_port_range_last"); sprintf(which, "TCP"); if (port_first < 0 || port_first > 65535) { printf("Error: Invalid first %s port: %d\n", which, port_first); exit_error_conf(cfg); } if (port_last < 0 || port_last > 65535) { printf("Error: Invalid last %s port: %d\n", which, port_last); exit_error_conf(cfg); } if (port_first > port_last) { printf("Error: First %s port is greater than last port.\n", which); exit_error_conf(cfg); } cs.ipv6_tcp_port_range_first = port_first; cs.ipv6_tcp_port_range_last = port_last; printf("\t%s pool port range: %d-%d\n", which, port_first, port_last); // port_first = cfg_getint(cfg_ipv6, "ipv6_udp_port_range_first"); port_last = cfg_getint(cfg_ipv6, "ipv6_udp_port_range_last"); sprintf(which, "UDP"); if (port_first < 0 || port_first > 65535) { printf("Error: Invalid first %s port: %d\n", which, port_first); exit_error_conf(cfg); } if (port_last < 0 || port_last > 65535) { printf("Error: Invalid last %s port: %d\n", which, port_last); exit_error_conf(cfg); } if (port_first > port_last) { printf("Error: First %s port is greater than last port.\n", which); exit_error_conf(cfg); } cs.ipv6_udp_port_range_first = port_first; cs.ipv6_udp_port_range_last = port_last; printf("\t%s pool port range: %d-%d\n", which, port_first, port_last); printf ("\n" ); } cfg_free(cfg); /* We got the configuration structure, now send it to the module * using netlink sockets. */ // Reserve memory for netlink socket nls = nl_socket_alloc(); if (!nls) { printf("bad nl_socket_alloc\n"); return EXIT_FAILURE; } // Bind and connect the socket to a protocol ret = nl_connect(nls, NETLINK_USERSOCK); if (ret < 0) { nl_perror(ret, "nl_connect"); nl_socket_free(nls); return EXIT_FAILURE; } ret = nl_send_simple(nls, MY_MSG_TYPE, 0, &(cs), sizeof(cs)); if (ret < 0) { nl_perror(ret, "nl_send_simple"); printf("Error sending message, is module loaded?\n"); nl_close(nls); nl_socket_free(nls); return EXIT_FAILURE; } else { printf("Message sent (%d bytes):\n", ret); //print_nat64_run_conf(nrc); } nl_close(nls); nl_socket_free(nls); exit(EXIT_SUCCESS); }
/** * Main function of the user app that parses configuration file and sends it to * NAT64 kernel module. * * @param argc Qty of arguments in command line call. * @param argv Array of arguments in command line call. */ int main(int argc, char *argv[]) { struct stat sb; // To check if config file exist. struct in_addr iaddrn, iaddrf, iaddrl; // To validate IP addresses struct in6_addr i6addrf; // also cfg_t *cfg, *cfg_ipv4, *cfg_ipv6; const char *sect_name; char *addr_first, *addr_last; unsigned char addr_maskbits; int port_first, port_last; char which[sizeof("ABC")]; char str[INET_ADDRSTRLEN]; struct config_struct cs; struct nl_sock *nls; int ret; int i = 0; struct ipv6_prefixes **ipv6_pref = NULL; unsigned char ipv6_pref_qty; char ipv6_def_prefix64[sizeof("1111:2222:3333:4444:5555:6666::/128")]; char *ipv6_buf; char *ipv6_check_addr; char *ipv6_check_maskbits; if (argc != 2) { printf("Usage: %s <config-file>\n", argv[0]); exit(EXIT_FAILURE); } if ( stat(argv[1], &sb) == -1 ) { printf("Error: Can not open configuration file: %s\n", argv[1]); exit(EXIT_FAILURE); } // Load default configuration values for each config option, just in case // they were not included in the config file. cfg_opt_t ipv4_opts[] = { CFG_STR("ipv4_addr_net", IPV4_DEF_NET, CFGF_NONE), CFG_INT("ipv4_addr_net_mask_bits", IPV4_DEF_MASKBITS, CFGF_NONE), CFG_STR("ipv4_pool_range_first", IPV4_DEF_POOL_FIRST, CFGF_NONE), CFG_STR("ipv4_pool_range_last", IPV4_DEF_POOL_LAST, CFGF_NONE), CFG_INT("ipv4_tcp_port_range_first", IPV4_DEF_TCP_PORTS_FIRST, CFGF_NONE), CFG_INT("ipv4_tcp_port_range_last", IPV4_DEF_TCP_PORTS_LAST, CFGF_NONE), CFG_INT("ipv4_udp_port_range_first", IPV4_DEF_UDP_PORTS_FIRST, CFGF_NONE), CFG_INT("ipv4_udp_port_range_last", IPV4_DEF_UDP_PORTS_LAST, CFGF_NONE), CFG_END() }; // Load default configuration values for each config option, just in case // they were not included in the config file. sprintf(ipv6_def_prefix64, "%s/%d", IPV6_DEF_PREFIX, IPV6_DEF_MASKBITS ); cfg_opt_t ipv6_opts[] = { CFG_STR_LIST("ipv6_net_prefixes", ipv6_def_prefix64, CFGF_NONE), CFG_INT("ipv6_tcp_port_range_first", IPV6_DEF_TCP_PORTS_FIRST, CFGF_NONE), CFG_INT("ipv6_tcp_port_range_last", IPV6_DEF_TCP_PORTS_LAST, CFGF_NONE), CFG_INT("ipv6_udp_port_range_first", IPV6_DEF_UDP_PORTS_FIRST, CFGF_NONE), CFG_INT("ipv6_udp_port_range_last", IPV6_DEF_UDP_PORTS_LAST, CFGF_NONE), CFG_END() }; // Define two sections in config file cfg_opt_t opts[] = { CFG_SEC("ipv4", ipv4_opts, CFGF_NONE), CFG_SEC("ipv6", ipv6_opts, CFGF_NONE), CFG_END() }; // Parse config file cfg = cfg_init(opts, CFGF_NONE); if(cfg_parse(cfg, argv[1]) == CFG_PARSE_ERROR) { printf("Error parsing configuration file: %s\n", argv[1]); exit_error_conf(cfg); } /* * Loading IPv4 configuration * */ { cfg_ipv4 = cfg_getsec(cfg, "ipv4"); sect_name = cfg_name(cfg_ipv4); printf ("Section: %s\n", sect_name); // Validate IPv4 pool address addr_first = cfg_getstr(cfg_ipv4, "ipv4_addr_net"); if ( convert_ipv4_addr(addr_first, &iaddrn) == EXIT_FAILURE ) { printf("Error: Invalid IPv4 address net: %s\n", addr_first); exit_error_conf(cfg); } // Validate netmask bits addr_maskbits = cfg_getint(cfg_ipv4, "ipv4_addr_net_mask_bits"); if ( validate_ipv4_netmask_bits(addr_maskbits) == EXIT_FAILURE ) { printf("Error: Bad IPv4 network mask bits value: %d\n", addr_maskbits); exit_error_conf(cfg); } // Store values in config struct cs.ipv4_addr_net = iaddrn; cs.ipv4_addr_net_mask_bits = addr_maskbits; printf("\tPool Network: %s/%d\n", addr_first, addr_maskbits); // Validate pool addresses range addr_first = cfg_getstr(cfg_ipv4, "ipv4_pool_range_first"); addr_last = cfg_getstr(cfg_ipv4, "ipv4_pool_range_last"); if ( convert_ipv4_addr(addr_first, &iaddrf) == EXIT_FAILURE ) // Validate ipv4 addr { printf("Error: Malformed ipv4_pool_range_first: %s\n", addr_first); exit_error_conf(cfg); } if ( convert_ipv4_addr(addr_last, &iaddrl) == EXIT_FAILURE ) // Validate ipv4 addr { printf("Error: Malformed ipv4_pool_range_last: %s\n", addr_last); exit_error_conf(cfg); } if ( validate_ipv4_pool_range(&iaddrn, addr_maskbits, &iaddrf, &iaddrl) == EXIT_FAILURE ) // Validate that: first < last { printf("Error: Pool addresses badly defined.\n"); exit_error_conf(cfg); } // Store values in config struct cs.ipv4_pool_range_first = iaddrf; cs.ipv4_pool_range_last = iaddrl; inet_ntop(AF_INET, &(iaddrf.s_addr), str, INET_ADDRSTRLEN); printf("\t\t- First address: %s\n", str); inet_ntop(AF_INET, &(iaddrl.s_addr), str, INET_ADDRSTRLEN); printf("\t\t- Last address: %s\n", str); // Validate port ranges port_first = cfg_getint(cfg_ipv4, "ipv4_tcp_port_range_first"); port_last = cfg_getint(cfg_ipv4, "ipv4_tcp_port_range_last"); sprintf(which, "TCP"); if ( validate_ports_range(port_first, port_last) == EXIT_FAILURE ) { //~ printf("Error: Invalid first %s port: %d\n", which, port_first); printf("Error: Invalid %s ports range.\n", which); exit_error_conf(cfg); } cs.ipv4_tcp_port_first = port_first; cs.ipv4_tcp_port_last = port_last; printf("\t%s pool port range: %d-%d\n", which, port_first, port_last); // port_first = cfg_getint(cfg_ipv4, "ipv4_udp_port_range_first"); port_last = cfg_getint(cfg_ipv4, "ipv4_udp_port_range_last"); sprintf(which, "UDP"); if ( validate_ports_range(port_first, port_last) == EXIT_FAILURE ) { printf("Error: Invalid %s ports range.\n", which); exit_error_conf(cfg); } cs.ipv4_udp_port_first = port_first; cs.ipv4_udp_port_last = port_last; printf("\t%s pool port range: %d-%d\n", which, port_first, port_last); printf ("\n" ); } /* * Loading IPv6 configuration * */ { cfg_ipv6 = cfg_getsec(cfg, "ipv6"); sect_name = cfg_name(cfg_ipv6); printf ("Section: %s\n", sect_name ); // Get number of IPv6 prefixes. ipv6_pref_qty = cfg_size(cfg_ipv6, "ipv6_net_prefixes"); // Allocate memory for the array of prefixes. ipv6_pref = (struct ipv6_prefixes **) malloc(ipv6_pref_qty * sizeof(struct ipv6_prefixes *)); for(i = 0; i < ipv6_pref_qty; i++) { // Split prefix and netmask bits ipv6_buf = cfg_getnstr(cfg_ipv6, "ipv6_net_prefixes", i); ipv6_check_addr = strtok(ipv6_buf, "/"); ipv6_check_maskbits = strtok(NULL, "/"); // Validate IPv6 addr if ( convert_ipv6_addr(ipv6_check_addr, &i6addrf) == EXIT_FAILURE ) { printf("Error: Invalid IPv6 address net: %s\n", ipv6_check_addr); exit_error_conf(cfg); } // Validate netmask bits addr_maskbits = atoi(ipv6_check_maskbits); if ( validate_ipv6_netmask_bits(addr_maskbits) == EXIT_FAILURE ) { printf("Error: Bad IPv6 network mask bits value: %d\n", addr_maskbits); exit_error_conf(cfg); } // Allocate memory for each IPv6 prefix ipv6_pref[i] = (struct ipv6_prefixes *) malloc(sizeof(struct ipv6_prefixes)); ipv6_pref[i]->addr = (i6addrf); ipv6_pref[i]->maskbits = addr_maskbits; } // Store prefixes in the config struct cs.ipv6_net_prefixes = ipv6_pref; cs.ipv6_net_prefixes_qty = ipv6_pref_qty; // Validate port ranges port_first = cfg_getint(cfg_ipv6, "ipv6_tcp_port_range_first"); port_last = cfg_getint(cfg_ipv6, "ipv6_tcp_port_range_last"); sprintf(which, "TCP"); if ( validate_ports_range(port_first, port_last) == EXIT_FAILURE ) { printf("Error: Invalid %s ports range.\n", which); exit_error_conf(cfg); } cs.ipv6_tcp_port_range_first = port_first; cs.ipv6_tcp_port_range_last = port_last; printf("\t%s pool port range: %d-%d\n", which, port_first, port_last); // port_first = cfg_getint(cfg_ipv6, "ipv6_udp_port_range_first"); port_last = cfg_getint(cfg_ipv6, "ipv6_udp_port_range_last"); sprintf(which, "UDP"); if ( validate_ports_range(port_first, port_last) == EXIT_FAILURE ) { printf("Error: Invalid %s ports range.\n", which); exit_error_conf(cfg); } cs.ipv6_udp_port_range_first = port_first; cs.ipv6_udp_port_range_last = port_last; printf("\t%s pool port range: %d-%d\n", which, port_first, port_last); printf ("\n" ); } cfg_free(cfg); /* We got the configuration structure, now send it to the module * using netlink sockets. */ // Reserve memory for netlink socket nls = nl_socket_alloc(); if (!nls) { printf("bad nl_socket_alloc\n"); return EXIT_FAILURE; } // Bind and connect the socket to kernel ret = nl_connect(nls, NETLINK_USERSOCK); if (ret < 0) { nl_perror(ret, "nl_connect"); nl_socket_free(nls); return EXIT_FAILURE; } // Send socket to module ret = nl_send_simple(nls, MSG_TYPE_CONF, 0, &(cs), sizeof(cs)); if (ret < 0) { nl_perror(ret, "nl_send_simple"); printf("Error sending message, is module loaded?\n"); nl_close(nls); nl_socket_free(nls); return EXIT_FAILURE; } else { printf("Message sent (%d bytes):\n", ret); //print_nat64_run_conf(nrc); } nl_close(nls); nl_socket_free(nls); exit(EXIT_SUCCESS); }