123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635 |
- /* This file is part of Netsukuku
- * (c) Copyright 2005 Andrea Lo Pumo aka AlpT <alpt@freaknet.org>
- *
- * This source code is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2 of the License,
- * or (at your option) any later version.
- *
- * This source code is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- * Please refer to the GNU Public License for more details.
- *
- * You should have received a copy of the GNU Public License along with
- * this source code; if not, write to:
- * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * -
- *
- * Various parts are ripped from iproute2/iproute.c
- * written by Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>.
- */
-
- #include "includes.h"
-
- #include "if.h"
- #include "libnetlink.h"
- #include "inet.h"
- #include "krnl_route.h"
- #include "libnetlink.h"
- #include "ll_map.h"
- #include "common.h"
-
- #ifdef LINUX_2_6_14
- #include <linux/ip_mp_alg.h>
- #define NTK_MULTIPATH_ALGO IP_MP_ALG_WRANDOM
- #endif
-
- static struct {
- int tb;
- int flushed;
- char *flushb;
- int flushp;
- int flushe;
- struct rtnl_handle *rth;
- int protocol, protocolmask;
- int scope, scopemask;
- int type, typemask;
- int tos, tosmask;
- int iif, iifmask;
- int oif, oifmask;
- int realm, realmmask;
- inet_prefix rprefsrc;
- inet_prefix rvia;
- inet_prefix rdst;
- inet_prefix mdst;
- inet_prefix rsrc;
- inet_prefix msrc;
- } filter;
-
- void
- route_reset_filter()
- {
- setzero(&filter, sizeof(filter));
- filter.mdst.bits = -1;
- filter.msrc.bits = -1;
- }
-
- int route_exec(int route_cmd, int route_type, int route_scope,
- unsigned flags, inet_prefix * src, inet_prefix * to,
- struct nexthop *nhops, char *dev, u_char table);
-
- int
- route_add(ROUTE_CMD_VARS)
- {
- return route_exec(RTM_NEWROUTE, type, scope, NLM_F_CREATE | NLM_F_EXCL,
- src, to, nhops, dev, table);
- }
-
- int
- route_del(ROUTE_CMD_VARS)
- {
- return route_exec(RTM_DELROUTE, type, scope, 0, src, to, nhops, dev,
- table);
- }
-
- /*If it doesn't exist, CREATE IT! de ih oh oh*/
- int
- route_replace(ROUTE_CMD_VARS)
- {
- return route_exec(RTM_NEWROUTE, type, scope,
- NLM_F_REPLACE | NLM_F_CREATE, src, to, nhops, dev,
- table);
- }
-
- int
- route_change(ROUTE_CMD_VARS)
- {
- return route_exec(RTM_NEWROUTE, type, scope, NLM_F_REPLACE, src, to,
- nhops, dev, table);
- }
-
- int
- route_append(ROUTE_CMD_VARS)
- {
- return route_exec(RTM_NEWROUTE, type, scope,
- NLM_F_CREATE | NLM_F_APPEND, src, to, nhops, dev,
- table);
- }
-
- int
- add_nexthops(struct nlmsghdr *n, struct rtmsg *r, struct nexthop *nhop)
- {
- char buf[1024];
- struct rtattr *rta = (void *) buf;
- struct rtnexthop *rtnh;
- int i = 0, idx;
-
- rta->rta_type = RTA_MULTIPATH;
- rta->rta_len = RTA_LENGTH(0);
- rtnh = RTA_DATA(rta);
-
- if (!nhop[i + 1].dev) {
- /* Just one gateway */
- r->rtm_family = nhop[i].gw.family;
- addattr_l(n, sizeof(struct rt_request), RTA_GATEWAY,
- &nhop[i].gw.data, nhop[i].gw.len);
-
- if (nhop[0].dev) {
- if ((idx = ll_name_to_index(nhop[0].dev)) == 0) {
- error(ERROR_MSG "Device \"%s\" doesn't really exist\n",
- ERROR_POS, nhop[0].dev);
- return -1;
- }
- addattr32(n, sizeof(struct rt_request), RTA_OIF, idx);
- }
-
- return 0;
- }
- #if 0
- /* We have more than one nexthop, equalize them */
- req.rt.rtm_flags |= RTM_F_EQUALIZE;
- #endif
-
- while (nhop[i].dev != 0) {
- setzero(rtnh, sizeof(*rtnh));
- rtnh->rtnh_len = sizeof(*rtnh);
- rta->rta_len += rtnh->rtnh_len;
-
- if (nhop[i].gw.len) {
- if (nhop[i].gw.family == AF_INET)
- rta_addattr32(rta, 4096, RTA_GATEWAY, nhop[i].gw.data[0]);
- else if (nhop[i].gw.family == AF_INET6)
- rta_addattr_l(rta, 4096, RTA_GATEWAY, nhop[i].gw.data,
- nhop[i].gw.len);
- rtnh->rtnh_len += sizeof(struct rtattr) + nhop[i].gw.len;
- }
-
- if (nhop[i].dev)
- if ((rtnh->rtnh_ifindex = ll_name_to_index(nhop[i].dev)) == 0)
- fatal("%s:%d, Cannot find device \"%s\"\n", ERROR_POS,
- nhop[i].dev);
-
- if (nhop[i].hops == 0) {
- debug(DBG_NORMAL, "hops=%d is invalid. Using hops=255\n",
- nhop[i].hops);
- rtnh->rtnh_hops = 255;
- } else
- rtnh->rtnh_hops = nhop[i].hops - 1;
-
- rtnh = RTNH_NEXT(rtnh);
- i++;
- }
-
- if (rta->rta_len > RTA_LENGTH(0))
- addattr_l(n, 1024, RTA_MULTIPATH, RTA_DATA(rta), RTA_PAYLOAD(rta));
- return 0;
- }
-
- /*
- * route_exec: replaces, adds or deletes a route from the routing table.
- * `to' and nhops->gw must be addresses given in network order
- */
- int
- route_exec(int route_cmd, int route_type, int route_scope, unsigned flags,
- inet_prefix * src, inet_prefix * to, struct nexthop *nhops,
- char *dev, u_char table)
- {
- struct rt_request req;
- struct rtnl_handle rth;
-
- setzero(&req, sizeof(req));
-
- if (!table)
- table = RT_TABLE_MAIN;
-
- req.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
- req.nh.nlmsg_flags = NLM_F_REQUEST | flags;
- req.nh.nlmsg_type = route_cmd;
- req.rt.rtm_family = AF_UNSPEC;
- req.rt.rtm_table = table;
- req.rt.rtm_protocol = RTPROT_NETSUKUKU;
- req.rt.rtm_scope = RT_SCOPE_NOWHERE;
- req.rt.rtm_type = RTN_UNSPEC;
-
- /* kernel protocol layer */
- if (table == RT_TABLE_LOCAL)
- req.rt.rtm_protocol = RTPROT_KERNEL;
-
- if (route_cmd != RTM_DELROUTE) {
- req.rt.rtm_scope = RT_SCOPE_UNIVERSE;
- req.rt.rtm_type = RTN_UNICAST;
- }
-
- if (route_type)
- req.rt.rtm_type = route_type;
-
- if (route_scope)
- req.rt.rtm_scope = route_scope;
- else if (req.rt.rtm_type == RTN_LOCAL)
- req.rt.rtm_scope = RT_SCOPE_HOST;
-
-
- if (rtnl_open(&rth, 0) < 0)
- return -1;
-
- if (dev || nhops)
- ll_init_map(&rth);
-
- #ifdef LINUX_2_6_14
- uint32_t mp_alg = NTK_MULTIPATH_ALGO;
- addattr_l(&req.n, sizeof(req), RTA_MP_ALGO, &mp_alg, sizeof(mp_alg));
- #endif
-
- if (dev) {
- int idx;
-
- if ((idx = ll_name_to_index(dev)) == 0) {
- error("%s:%d, Device \"%s\" doesn't really exist\n", ERROR_POS,
- dev);
- return -1;
- }
- addattr32(&req.nh, sizeof(req), RTA_OIF, idx);
- }
-
- if (to) {
- req.rt.rtm_family = to->family;
- req.rt.rtm_dst_len = to->bits;
-
- if (!to->data[0] && !to->data[1] && !to->data[2] && !to->data[3]) {
- /* Modify the default gw */
- if (route_cmd == RTM_DELROUTE)
- req.rt.rtm_protocol = 0;
- }
-
- if (to->len)
- addattr_l(&req.nh, sizeof(req), RTA_DST, &to->data, to->len);
- }
-
- if (src) {
- if (req.rt.rtm_family == AF_UNSPEC)
- req.rt.rtm_family = src->family;
- addattr_l(&req.nh, sizeof(req), RTA_PREFSRC, &src->data, src->len);
- }
-
- if (nhops)
- add_nexthops(&req.nh, &req.rt, nhops);
-
- if (req.rt.rtm_family == AF_UNSPEC)
- req.rt.rtm_family = AF_INET;
-
- /*Finaly stage: <<Hey krnl, r u there?>> */
- if (rtnl_talk(&rth, &req.nh, 0, 0, NULL, NULL, NULL) < 0)
- return -1;
-
- rtnl_close(&rth);
-
- return 0;
- }
-
- /*
- * route_get_gw: if the route stored in `who' and `n' is matched by the
- * `filter', it stores the gateway address of that route in `arg', which
- * is a pointer to an inet_prefix struct. The address is stored in host order.
- * The dev name of the route is appended at `arg'+sizeof(inet_prefix).
- * Only the non-deleted routes are considered.
- */
- int
- route_get_gw(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg)
- {
- struct rtmsg *r = NLMSG_DATA(n);
- int len = n->nlmsg_len;
- struct rtattr *tb[RTA_MAX + 1];
- inet_prefix dst;
- inet_prefix via;
- int host_len = -1;
-
-
- if (n->nlmsg_type != RTM_NEWROUTE && n->nlmsg_type != RTM_DELROUTE)
- return 0;
- if (filter.flushb && n->nlmsg_type != RTM_NEWROUTE)
- return 0;
- len -= NLMSG_LENGTH(sizeof(*r));
- if (len < 0)
- return -1;
-
- if (r->rtm_family == AF_INET6)
- host_len = 128;
- else if (r->rtm_family == AF_INET)
- host_len = 32;
- else if (r->rtm_family == AF_DECnet)
- host_len = 16;
- else if (r->rtm_family == AF_IPX)
- host_len = 80;
-
- if (r->rtm_family == AF_INET6) {
- if (filter.tb) {
- if (filter.tb < 0) {
- if (!(r->rtm_flags & RTM_F_CLONED))
- return 0;
- } else {
- if (r->rtm_flags & RTM_F_CLONED)
- return 0;
- if (filter.tb == RT_TABLE_LOCAL) {
- if (r->rtm_type != RTN_LOCAL)
- return 0;
- } else if (filter.tb == RT_TABLE_MAIN) {
- if (r->rtm_type == RTN_LOCAL)
- return 0;
- } else {
- return 0;
- }
- }
- }
- } else {
- if (filter.tb > 0 && filter.tb != r->rtm_table)
- return 0;
- }
- if ((filter.protocol ^ r->rtm_protocol) & filter.protocolmask)
- return 0;
- if ((filter.scope ^ r->rtm_scope) & filter.scopemask)
- return 0;
- if ((filter.type ^ r->rtm_type) & filter.typemask)
- return 0;
- if ((filter.tos ^ r->rtm_tos) & filter.tosmask)
- return 0;
- if (filter.rdst.family &&
- (r->rtm_family != filter.rdst.family
- || filter.rdst.bits > r->rtm_dst_len))
- return 0;
- if (filter.mdst.family &&
- (r->rtm_family != filter.mdst.family ||
- (filter.mdst.bits < r->rtm_dst_len)))
- return 0;
- if (filter.rsrc.family &&
- (r->rtm_family != filter.rsrc.family
- || filter.rsrc.bits > r->rtm_src_len))
- return 0;
- if (filter.msrc.family &&
- (r->rtm_family != filter.msrc.family ||
- (filter.msrc.bits < r->rtm_src_len)))
- return 0;
- if (filter.rvia.family && r->rtm_family != filter.rvia.family)
- return 0;
- if (filter.rprefsrc.family && r->rtm_family != filter.rprefsrc.family)
- return 0;
-
- parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
-
- setzero(&dst, sizeof(dst));
- dst.family = r->rtm_family;
- if (tb[RTA_DST]) {
- memcpy(&dst.data, RTA_DATA(tb[RTA_DST]), (r->rtm_dst_len + 7) / 8);
- }
- if (filter.rdst.family
- && inet_addr_match(&dst, &filter.rdst, filter.rdst.bits))
- return 0;
- if (filter.mdst.family &&
- inet_addr_match(&dst, &filter.mdst, r->rtm_dst_len))
- return 0;
-
- if (n->nlmsg_type == RTM_DELROUTE)
- return 0;
-
- /*
- * ... and finally if all the tests passed, copy the gateway address
- */
- if (tb[RTA_GATEWAY]) {
- memcpy(&via.data, RTA_DATA(tb[RTA_GATEWAY]), host_len / 8);
- via.family = r->rtm_family;
- inet_setip(arg, (u_int *) & via.data, via.family);
- } else if (tb[RTA_MULTIPATH]) {
- struct rtnexthop *nh = RTA_DATA(tb[RTA_MULTIPATH]);
-
- len = RTA_PAYLOAD(tb[RTA_MULTIPATH]);
-
- for (;;) {
- if (len < sizeof(*nh))
- break;
- if (nh->rtnh_len > len)
- break;
- if (r->rtm_flags & RTM_F_CLONED
- && r->rtm_type == RTN_MULTICAST)
- goto skip_nexthop;
-
- if (nh->rtnh_len > sizeof(*nh)) {
- parse_rtattr(tb, RTA_MAX, RTNH_DATA(nh),
- nh->rtnh_len - sizeof(*nh));
- if (tb[RTA_GATEWAY]) {
- memcpy(&via.data, RTA_DATA(tb[RTA_GATEWAY]),
- host_len / 8);
- via.family = r->rtm_family;
- inet_setip(arg, (u_int *) & via.data, via.family);
-
- /* Copy the interface name */
- strncpy((char *) arg + sizeof(inet_prefix),
- ll_index_to_name(nh->rtnh_ifindex), IFNAMSIZ);
- break;
- }
- }
- skip_nexthop:
- len -= NLMSG_ALIGN(nh->rtnh_len);
- nh = RTNH_NEXT(nh);
- }
- }
-
-
- /* Copy the interface name */
- if (tb[RTA_OIF] && filter.oifmask != -1)
- strncpy((char *) arg + sizeof(inet_prefix),
- ll_index_to_name(*(int *) RTA_DATA(tb[RTA_OIF])),
- IFNAMSIZ);
-
- return 0;
- }
-
- /*
- * route_get_exact_prefix: it dumps the routing table and search for a route
- * which has the prefix equal to `prefix', if it is found its destination
- * address is stored in `dst' and its interface name in `dev_name' (which must
- * be IFNAMSIZ big).
- */
- int
- route_get_exact_prefix_dst(inet_prefix prefix, inet_prefix * dst,
- char *dev_name)
- {
- int do_ipv6 = AF_UNSPEC;
- struct rtnl_handle rth;
- char dst_data[sizeof(inet_prefix) + IFNAMSIZ];
-
- route_reset_filter();
- filter.tb = RT_TABLE_MAIN;
-
- filter.mdst = prefix;
- filter.rdst = filter.mdst;
-
- if (do_ipv6 == AF_UNSPEC && filter.tb)
- do_ipv6 = AF_INET;
-
- if (rtnl_open(&rth, 0) < 0)
- return -1;
-
- ll_init_map(&rth);
-
- if (rtnl_wilddump_request(&rth, do_ipv6, RTM_GETROUTE) < 0) {
- error(ERROR_MSG "Cannot send dump request" ERROR_POS);
- return -1;
- }
-
- setzero(dst_data, sizeof(dst_data));
- if (rtnl_dump_filter(&rth, route_get_gw, dst_data, NULL, NULL) < 0) {
- debug(DBG_NORMAL, ERROR_MSG "Dump terminated" ERROR_POS);
- return -1;
- }
- inet_copy(dst, (inet_prefix *) dst_data);
- memcpy(dev_name, dst_data + sizeof(inet_prefix), IFNAMSIZ);
-
- rtnl_close(&rth);
-
- return 0;
- }
-
- int
- route_flush_cache(int family)
- {
- int len, err;
- int flush_fd;
- char ROUTE_FLUSH_SYSCTL[] = "/proc/sys/net/ipvX/route/flush";
- char *buf = "-1";
-
- len = strlen(buf);
- if (family == AF_INET)
- ROUTE_FLUSH_SYSCTL[17] = '4';
- else if (family == AF_INET6)
- ROUTE_FLUSH_SYSCTL[17] = '6';
- else
- return -1;
-
- flush_fd = open(ROUTE_FLUSH_SYSCTL, O_WRONLY);
- if (flush_fd < 0) {
- debug(DBG_NORMAL, "Cannot open \"%s\"\n", ROUTE_FLUSH_SYSCTL);
- return -1;
- }
-
- if ((err = write(flush_fd, (void *) buf, len)) == 0) {
- debug(DBG_NORMAL, "Warning: Route Cache not flushed\n");
- return -1;
- } else if (err == -1) {
- debug(DBG_NORMAL, "Cannot flush routing cache: %s\n",
- strerror(errno));
- return -1;
- }
- close(flush_fd);
-
- return 0;
- }
-
- int
- route_ip_forward(int family, int enable)
- {
- int len, err;
- int flush_fd;
- char *ROUTE_FORWARD_SYSCTL = "/proc/sys/net/ipv4/ip_forward";
- char *ROUTE_FORWARD_SYSCTL_6 =
- "/proc/sys/net/ipv6/conf/all/forwarding";
- char *sysctl_path, buf[2];
-
- buf[0] = '1';
- buf[1] = 0;
-
- len = strlen(buf);
- if (family == AF_INET)
- sysctl_path = ROUTE_FORWARD_SYSCTL;
- else if (family == AF_INET6)
- sysctl_path = ROUTE_FORWARD_SYSCTL_6;
- else
- return -1;
-
- if (!enable)
- buf[0] = '0';
-
- flush_fd = open(sysctl_path, O_WRONLY);
- if (flush_fd < 0) {
- debug(DBG_NORMAL, "Cannot open \"%s\"\n", sysctl_path);
- return -1;
- }
-
- if ((err = write(flush_fd, (void *) buf, len)) == 0) {
- debug(DBG_NORMAL, "Warning: ip_forward setting changed\n");
- return -1;
- } else if (err == -1) {
- debug(DBG_NORMAL, "Cannot change the ip_forward setting: %s\n",
- strerror(errno));
- return -1;
- }
- close(flush_fd);
-
- return 0;
- }
-
- /*
- * route_rp_filter
- *
- * Modifies the /proc/sys/net/ipv4/conf/INTERFACE/rp_filter config file.
- */
- int
- route_rp_filter(int family, char *dev, int enable)
- {
- int len, err, ret = 0;
- int flush_fd;
-
- /* The path is /proc/sys/net/ipv4/conf/INTERFACE/rp_filter */
- const char *RP_FILTER_SYSCTL_1 = "/proc/sys/net/ipv4/conf/";
- const char *RP_FILTER_SYSCTL_1_IPV6 = "/proc/sys/net/ipv6/conf/";
- const char *RP_FILTER_SYSCTL_2 = "/rp_filter";
- char *final_path = 0, buf[2];
-
- buf[0] = '1';
- buf[1] = 0;
- #define RP_FILTER_PATH_SZ (strlen(RP_FILTER_SYSCTL_1)+ \
- strlen(RP_FILTER_SYSCTL_2)+IF_NAMESIZE+1)
- final_path = xzalloc(RP_FILTER_PATH_SZ);
-
- len = strlen(buf);
- if (family == AF_INET) {
- strcpy(final_path, RP_FILTER_SYSCTL_1);
- } else if (family == AF_INET6) {
- strcpy(final_path, RP_FILTER_SYSCTL_1_IPV6);
- } else
- ERROR_FINISH(ret, -1, finish);
-
- strcat(final_path, dev);
- strcat(final_path, RP_FILTER_SYSCTL_2);
-
- if (!enable)
- buf[0] = '0';
-
- flush_fd = open(final_path, O_WRONLY);
- if (flush_fd < 0) {
- debug(DBG_NORMAL, "Cannot open \"%s\"\n", final_path);
- ERROR_FINISH(ret, -1, finish);
- }
-
- if ((err = write(flush_fd, (void *) buf, len)) == 0) {
- debug(DBG_NORMAL, "Warning: rp_filter setting changed\n");
- ERROR_FINISH(ret, -1, finish);
- } else if (err == -1) {
- debug(DBG_NORMAL, "Cannot change the rp_filter setting: %s\n",
- strerror(errno));
- ERROR_FINISH(ret, -1, finish);
- }
- close(flush_fd);
-
- finish:
- if (final_path)
- xfree(final_path);
- return ret;
- }
-
- /*
- * route_rp_filter_all_dev: do route_rp_filter() for all the interfaces
- * present in the `ifs' array.
- */
- int
- route_rp_filter_all_dev(int family, interface * ifs, int ifs_n, int enable)
- {
- int i, ret = 0;
-
- for (i = 0; i < ifs_n; i++)
- ret += route_rp_filter(family, ifs[i].dev_name, enable);
-
- return ret;
- }
-
- /*Life is strange*/
|