+ if (tnl_cfg->in_key_flow && tnl_cfg->out_key_flow) {
+ smap_add(args, "key", "flow");
+ } else if (tnl_cfg->in_key_present && tnl_cfg->out_key_present
+ && tnl_cfg->in_key == tnl_cfg->out_key) {
+ smap_add_format(args, "key", "%"PRIu64, ntohll(tnl_cfg->in_key));
+ } else {
+ if (tnl_cfg->in_key_flow) {
+ smap_add(args, "in_key", "flow");
+ } else if (tnl_cfg->in_key_present) {
+ smap_add_format(args, "in_key", "%"PRIu64,
+ ntohll(tnl_cfg->in_key));
+ }
+
+ if (tnl_cfg->out_key_flow) {
+ smap_add(args, "out_key", "flow");
+ } else if (tnl_cfg->out_key_present) {
+ smap_add_format(args, "out_key", "%"PRIu64,
+ ntohll(tnl_cfg->out_key));
+ }
+ }
+
+ if (tnl_cfg->ttl_inherit) {
+ smap_add(args, "ttl", "inherit");
+ } else if (tnl_cfg->ttl != DEFAULT_TTL) {
+ smap_add_format(args, "ttl", "%"PRIu8, tnl_cfg->ttl);
+ }
+
+ if (tnl_cfg->tos_inherit) {
+ smap_add(args, "tos", "inherit");
+ } else if (tnl_cfg->tos) {
+ smap_add_format(args, "tos", "0x%x", tnl_cfg->tos);
+ }
+
+ if (tnl_cfg->dst_port) {
+ uint16_t dst_port = ntohs(tnl_cfg->dst_port);
+ const char *type = netdev_get_type(dev);
+
+ if ((!strcmp("vxlan", type) && dst_port != VXLAN_DST_PORT) ||
+ (!strcmp("lisp", type) && dst_port != LISP_DST_PORT)) {
+ smap_add_format(args, "dst_port", "%d", dst_port);
+ }
+ }
+
+ if (tnl_cfg->csum) {
+ smap_add(args, "csum", "true");
+ }
+
+ if (!tnl_cfg->dont_fragment) {
+ smap_add(args, "df_default", "false");
+ }
+
+ return 0;
+}
+\f
+/* Code specific to patch ports. */
+
+const char *
+netdev_vport_patch_peer(const struct netdev *netdev)
+{
+ return (netdev_vport_is_patch(netdev)
+ ? netdev_vport_cast(netdev)->peer
+ : NULL);
+}
+
+void
+netdev_vport_inc_rx(const struct netdev *netdev,
+ const struct dpif_flow_stats *stats)
+{
+ if (is_vport_class(netdev_get_class(netdev))) {
+ struct netdev_vport *dev = netdev_vport_cast(netdev);
+ dev->stats.rx_packets += stats->n_packets;
+ dev->stats.rx_bytes += stats->n_bytes;
+ }
+}
+
+void
+netdev_vport_inc_tx(const struct netdev *netdev,
+ const struct dpif_flow_stats *stats)
+{
+ if (is_vport_class(netdev_get_class(netdev))) {
+ struct netdev_vport *dev = netdev_vport_cast(netdev);
+ dev->stats.tx_packets += stats->n_packets;
+ dev->stats.tx_bytes += stats->n_bytes;
+ }
+}
+
+static int
+get_patch_config(const struct netdev *dev_, struct smap *args)
+{
+ struct netdev_vport *dev = netdev_vport_cast(dev_);
+
+ if (dev->peer) {
+ smap_add(args, "peer", dev->peer);
+ }
+ return 0;
+}
+
+static int
+set_patch_config(struct netdev *dev_, const struct smap *args)
+{
+ struct netdev_vport *dev = netdev_vport_cast(dev_);
+ const char *name = netdev_get_name(dev_);
+ const char *peer;
+
+ peer = smap_get(args, "peer");
+ if (!peer) {
+ VLOG_ERR("%s: patch type requires valid 'peer' argument", name);