#include <linux/if.h>
#include <linux/ip.h>
#include <linux/if_tunnel.h>
+#include <linux/if_arp.h>
#include "utils.h"
#include "tunnel.h"
+#include "json_print.h"
const char *tnl_strproto(__u8 proto)
{
- static char buf[16];
-
switch (proto) {
case IPPROTO_IPIP:
- strcpy(buf, "ip");
- break;
+ return "ip";
case IPPROTO_GRE:
- strcpy(buf, "gre");
- break;
+ return "gre";
case IPPROTO_IPV6:
- strcpy(buf, "ipv6");
- break;
+ return "ipv6";
case IPPROTO_ESP:
- strcpy(buf, "esp");
- break;
+ return "esp";
case IPPROTO_MPLS:
- strcpy(buf, "mpls");
- break;
+ return "mpls";
case 0:
- strcpy(buf, "any");
- break;
+ return "any";
default:
- strcpy(buf, "unknown");
- break;
+ return "unknown";
}
-
- return buf;
}
int tnl_get_ioctl(const char *basedev, void *p)
int fd;
int err;
- strncpy(ifr.ifr_name, basedev, IFNAMSIZ);
+ strlcpy(ifr.ifr_name, basedev, IFNAMSIZ);
ifr.ifr_ifru.ifru_data = (void *)p;
fd = socket(preferred_family, SOCK_DGRAM, 0);
int err;
if (cmd == SIOCCHGTUNNEL && name[0])
- strncpy(ifr.ifr_name, name, IFNAMSIZ);
+ strlcpy(ifr.ifr_name, name, IFNAMSIZ);
else
- strncpy(ifr.ifr_name, basedev, IFNAMSIZ);
+ strlcpy(ifr.ifr_name, basedev, IFNAMSIZ);
ifr.ifr_ifru.ifru_data = p;
fd = socket(preferred_family, SOCK_DGRAM, 0);
int err;
if (name[0])
- strncpy(ifr.ifr_name, name, IFNAMSIZ);
+ strlcpy(ifr.ifr_name, name, IFNAMSIZ);
else
- strncpy(ifr.ifr_name, basedev, IFNAMSIZ);
+ strlcpy(ifr.ifr_name, basedev, IFNAMSIZ);
ifr.ifr_ifru.ifru_data = p;
int fd;
int err;
- strncpy(ifr.ifr_name, name, IFNAMSIZ);
+ strlcpy(ifr.ifr_name, name, IFNAMSIZ);
ifr.ifr_ifru.ifru_data = p;
fd = socket(preferred_family, SOCK_DGRAM, 0);
return get_addr32(key);
if (get_unsigned(&uval, key, 0) < 0) {
- fprintf(stderr, "invalid value for \"%s\": \"%s\";", name, key);
- fprintf(stderr, " it should be an unsigned integer\n");
+ fprintf(stderr,
+ "invalid value for \"%s\": \"%s\"; it should be an unsigned integer\n",
+ name, key);
exit(-1);
}
return htonl(uval);
}
-/* tnl_print_stats - print tunnel statistics
- *
- * @buf - tunnel interface's line in /proc/net/dev,
- * starting past the interface name and following colon
- */
-void tnl_print_stats(const char *buf)
+static const char *tnl_encap_str(const char *name, int enabled, int port)
+{
+ static const char ne[][sizeof("no")] = {
+ [0] = "no",
+ [1] = "",
+ };
+ static char buf[32];
+ char b1[16];
+ const char *val;
+
+ if (!port) {
+ val = "auto ";
+ } else if (port < 0) {
+ val = "";
+ } else {
+ snprintf(b1, sizeof(b1), "%u ", port - 1);
+ val = b1;
+ }
+
+ snprintf(buf, sizeof(buf), "%sencap-%s %s", ne[!!enabled], name, val);
+ return buf;
+}
+
+void tnl_print_encap(struct rtattr *tb[],
+ int encap_type, int encap_flags,
+ int encap_sport, int encap_dport)
{
- unsigned long rx_bytes, rx_packets, rx_errs, rx_drops,
- rx_fifo, rx_frame,
- tx_bytes, tx_packets, tx_errs, tx_drops,
- tx_fifo, tx_colls, tx_carrier, rx_multi;
-
- if (sscanf(buf, "%lu%lu%lu%lu%lu%lu%lu%*d%lu%lu%lu%lu%lu%lu%lu",
- &rx_bytes, &rx_packets, &rx_errs, &rx_drops,
- &rx_fifo, &rx_frame, &rx_multi,
- &tx_bytes, &tx_packets, &tx_errs, &tx_drops,
- &tx_fifo, &tx_colls, &tx_carrier) != 14)
+ __u16 type, flags, sport, dport;
+
+ if (!tb[encap_type])
+ return;
+
+ type = rta_getattr_u16(tb[encap_type]);
+ if (type == TUNNEL_ENCAP_NONE)
return;
+ flags = rta_getattr_u16(tb[encap_flags]);
+ sport = rta_getattr_u16(tb[encap_sport]);
+ dport = rta_getattr_u16(tb[encap_dport]);
+
+ open_json_object("encap");
+ print_string(PRINT_FP, NULL, "encap ", NULL);
+
+ switch (type) {
+ case TUNNEL_ENCAP_FOU:
+ print_string(PRINT_ANY, "type", "%s ", "fou");
+ break;
+ case TUNNEL_ENCAP_GUE:
+ print_string(PRINT_ANY, "type", "%s ", "gue");
+ break;
+ default:
+ print_null(PRINT_ANY, "type", "%s ", "unknown");
+ break;
+ }
+
+ if (is_json_context()) {
+ print_uint(PRINT_JSON, "sport", NULL, ntohs(sport));
+ print_uint(PRINT_JSON, "dport", NULL, ntohs(dport));
+ print_bool(PRINT_JSON, "csum", NULL,
+ flags & TUNNEL_ENCAP_FLAG_CSUM);
+ print_bool(PRINT_JSON, "csum6", NULL,
+ flags & TUNNEL_ENCAP_FLAG_CSUM6);
+ print_bool(PRINT_JSON, "remcsum", NULL,
+ flags & TUNNEL_ENCAP_FLAG_REMCSUM);
+ close_json_object();
+ } else {
+ int t;
+
+ t = sport ? ntohs(sport) + 1 : 0;
+ print_string(PRINT_FP, NULL, "%s",
+ tnl_encap_str("sport", 1, t));
+
+ t = ntohs(dport) + 1;
+ print_string(PRINT_FP, NULL, "%s",
+ tnl_encap_str("dport", 1, t));
+
+ t = flags & TUNNEL_ENCAP_FLAG_CSUM;
+ print_string(PRINT_FP, NULL, "%s",
+ tnl_encap_str("csum", t, -1));
+
+ t = flags & TUNNEL_ENCAP_FLAG_CSUM6;
+ print_string(PRINT_FP, NULL, "%s",
+ tnl_encap_str("csum6", t, -1));
+
+ t = flags & TUNNEL_ENCAP_FLAG_REMCSUM;
+ print_string(PRINT_FP, NULL, "%s",
+ tnl_encap_str("remcsum", t, -1));
+ }
+}
+
+void tnl_print_endpoint(const char *name, const struct rtattr *rta, int family)
+{
+ const char *value;
+ inet_prefix dst;
+
+ if (!rta) {
+ value = "any";
+ } else if (get_addr_rta(&dst, rta, family)) {
+ value = "unknown";
+ } else if (dst.flags & ADDRTYPE_UNSPEC) {
+ value = "any";
+ } else {
+ value = format_host(family, dst.bytelen, dst.data);
+ if (!value)
+ value = "unknown";
+ }
+
+ if (is_json_context()) {
+ print_string(PRINT_JSON, name, NULL, value);
+ } else {
+ SPRINT_BUF(b1);
+
+ snprintf(b1, sizeof(b1), "%s %%s ", name);
+ print_string(PRINT_FP, NULL, b1, value);
+ }
+}
+
+static void tnl_print_stats(const struct rtnl_link_stats64 *s)
+{
printf("%s", _SL_);
printf("RX: Packets Bytes Errors CsumErrs OutOfSeq Mcasts%s", _SL_);
- printf(" %-10ld %-12ld %-6ld %-8ld %-8ld %-8ld%s",
- rx_packets, rx_bytes, rx_errs, rx_frame, rx_fifo, rx_multi, _SL_);
+ printf(" %-10lld %-12lld %-6lld %-8lld %-8lld %-8lld%s",
+ s->rx_packets, s->rx_bytes, s->rx_errors, s->rx_frame_errors,
+ s->rx_fifo_errors, s->multicast, _SL_);
printf("TX: Packets Bytes Errors DeadLoop NoRoute NoBufs%s", _SL_);
- printf(" %-10ld %-12ld %-6ld %-8ld %-8ld %-6ld",
- tx_packets, tx_bytes, tx_errs, tx_colls, tx_carrier, tx_drops);
+ printf(" %-10lld %-12lld %-6lld %-8lld %-8lld %-6lld",
+ s->tx_packets, s->tx_bytes, s->tx_errors, s->collisions,
+ s->tx_carrier_errors, s->tx_dropped);
+}
+
+static int print_nlmsg_tunnel(struct nlmsghdr *n, void *arg)
+{
+ struct tnl_print_nlmsg_info *info = arg;
+ struct ifinfomsg *ifi = NLMSG_DATA(n);
+ struct rtattr *tb[IFLA_MAX+1];
+ const char *name, *n1;
+
+ if (n->nlmsg_type != RTM_NEWLINK && n->nlmsg_type != RTM_DELLINK)
+ return 0;
+
+ if (n->nlmsg_len < NLMSG_LENGTH(sizeof(*ifi)))
+ return -1;
+
+ if (preferred_family == AF_INET) {
+ switch (ifi->ifi_type) {
+ case ARPHRD_TUNNEL:
+ case ARPHRD_IPGRE:
+ case ARPHRD_SIT:
+ break;
+ default:
+ return 0;
+ }
+ } else {
+ switch (ifi->ifi_type) {
+ case ARPHRD_TUNNEL6:
+ case ARPHRD_IP6GRE:
+ break;
+ default:
+ return 0;
+ }
+ }
+
+ parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), IFLA_PAYLOAD(n));
+
+ if (!tb[IFLA_IFNAME])
+ return 0;
+
+ name = rta_getattr_str(tb[IFLA_IFNAME]);
+
+ /* Assume p1->name[IFNAMSIZ] is first field of structure */
+ n1 = info->p1;
+ if (n1[0] && strcmp(n1, name))
+ return 0;
+
+ info->ifi = ifi;
+ info->init(info);
+
+ /* TODO: parse netlink attributes */
+ if (tnl_get_ioctl(name, info->p2))
+ return 0;
+
+ if (!info->match(info))
+ return 0;
+
+ info->print(info->p2);
+ if (show_stats) {
+ struct rtnl_link_stats64 s;
+
+ if (get_rtnl_link_stats_rta(&s, tb) <= 0)
+ return -1;
+
+ tnl_print_stats(&s);
+ }
+ fputc('\n', stdout);
+
+ return 0;
+}
+
+int do_tunnels_list(struct tnl_print_nlmsg_info *info)
+{
+ if (rtnl_linkdump_req(&rth, preferred_family) < 0) {
+ perror("Cannot send dump request\n");
+ return -1;
+ }
+
+ if (rtnl_dump_filter(&rth, print_nlmsg_tunnel, info) < 0) {
+ fprintf(stderr, "Dump terminated\n");
+ return -1;
+ }
+
+ return 0;
}