-/* Copyright (c) 2013 Nicira, Inc.
+/* Copyright (c) 2013, 2014 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "hmap.h"
#include "list.h"
#include "netdev.h"
-#include "netlink.h"
#include "odp-util.h"
#include "ofpbuf.h"
#include "ovs-thread.h"
uint8_t eth_dst[ETH_ADDR_LEN];/* Ethernet destination address. */
bool eth_dst_set; /* 'eth_dst' set through database. */
+ ovs_be32 ip_src; /* IPv4 source address. */
+ ovs_be32 ip_dst; /* IPv4 destination address. */
+
uint16_t udp_src; /* UDP source port. */
/* All timers in milliseconds. */
int forwarding_override; /* Manual override of 'forwarding' status. */
atomic_bool check_tnl_key; /* Verify tunnel key of inbound packets? */
- atomic_int ref_cnt;
+ struct ovs_refcount ref_cnt;
/* When forward_if_rx is true, bfd_forwarding() will return
* true as long as there are incoming packets received.
long long int decay_detect_time; /* Decay detection time. */
uint64_t flap_count; /* Counts bfd forwarding flaps. */
+
+ /* True when the variables returned by bfd_get_status() are changed
+ * since last check. */
+ bool status_changed;
};
static struct ovs_mutex mutex = OVS_MUTEX_INITIALIZER;
static struct hmap all_bfds__ = HMAP_INITIALIZER(&all_bfds__);
static struct hmap *const all_bfds OVS_GUARDED_BY(mutex) = &all_bfds__;
+static bool bfd_lookup_ip(const char *host_name, struct in_addr *)
+ OVS_REQUIRES(mutex);
static bool bfd_forwarding__(struct bfd *) OVS_REQUIRES(mutex);
static bool bfd_in_poll(const struct bfd *) OVS_REQUIRES(mutex);
static void bfd_poll(struct bfd *bfd) OVS_REQUIRES(mutex);
static uint64_t bfd_rx_packets(const struct bfd *) OVS_REQUIRES(mutex);
static void bfd_try_decay(struct bfd *) OVS_REQUIRES(mutex);
static void bfd_decay_update(struct bfd *) OVS_REQUIRES(mutex);
+static void bfd_status_changed(struct bfd *) OVS_REQUIRES(mutex);
static void bfd_forwarding_if_rx_update(struct bfd *) OVS_REQUIRES(mutex);
static void bfd_unixctl_show(struct unixctl_conn *, int argc,
}
}
+/* Returns and resets the 'bfd->status_changed'. */
+bool
+bfd_check_status_change(struct bfd *bfd) OVS_EXCLUDED(mutex)
+{
+ bool ret;
+
+ ovs_mutex_lock(&mutex);
+ ret = bfd->status_changed;
+ bfd->status_changed = false;
+ ovs_mutex_unlock(&mutex);
+
+ return ret;
+}
+
/* Returns a 'smap' of key value pairs representing the status of 'bfd'
* intended for the OVS database. */
void
bool need_poll = false;
bool cfg_min_rx_changed = false;
bool cpath_down, forwarding_if_rx;
- const char *hwaddr;
+ const char *hwaddr, *ip_src, *ip_dst;
+ struct in_addr in_addr;
uint8_t ea[ETH_ADDR_LEN];
if (ovsthread_once_start(&once)) {
bfd->diag = DIAG_NONE;
bfd->min_tx = 1000;
bfd->mult = 3;
- atomic_init(&bfd->ref_cnt, 1);
+ ovs_refcount_init(&bfd->ref_cnt);
bfd->netdev = netdev_ref(netdev);
bfd->rx_packets = bfd_rx_packets(bfd);
bfd->in_decay = false;
cpath_down = smap_get_bool(cfg, "cpath_down", false);
if (bfd->cpath_down != cpath_down) {
bfd->cpath_down = cpath_down;
- if (bfd->diag == DIAG_NONE || bfd->diag == DIAG_CPATH_DOWN) {
- bfd_set_state(bfd, bfd->state, DIAG_NONE);
- }
+ bfd_set_state(bfd, bfd->state, DIAG_NONE);
need_poll = true;
}
bfd->eth_dst_set = false;
}
+ ip_src = smap_get(cfg, "bfd_src_ip");
+ if (ip_src && bfd_lookup_ip(ip_src, &in_addr)) {
+ memcpy(&bfd->ip_src, &in_addr, sizeof in_addr);
+ } else {
+ bfd->ip_src = htonl(0xA9FE0100); /* 169.254.1.0. */
+ }
+
+ ip_dst = smap_get(cfg, "bfd_dst_ip");
+ if (ip_dst && bfd_lookup_ip(ip_dst, &in_addr)) {
+ memcpy(&bfd->ip_dst, &in_addr, sizeof in_addr);
+ } else {
+ bfd->ip_dst = htonl(0xA9FE0101); /* 169.254.1.1. */
+ }
+
forwarding_if_rx = smap_get_bool(cfg, "forwarding_if_rx", false);
if (bfd->forwarding_if_rx != forwarding_if_rx) {
bfd->forwarding_if_rx = forwarding_if_rx;
{
struct bfd *bfd = CONST_CAST(struct bfd *, bfd_);
if (bfd) {
- int orig;
- atomic_add(&bfd->ref_cnt, 1, &orig);
- ovs_assert(orig > 0);
+ ovs_refcount_ref(&bfd->ref_cnt);
}
return bfd;
}
void
bfd_unref(struct bfd *bfd) OVS_EXCLUDED(mutex)
{
- if (bfd) {
- int orig;
-
- atomic_sub(&bfd->ref_cnt, 1, &orig);
- ovs_assert(orig > 0);
- if (orig == 1) {
- ovs_mutex_lock(&mutex);
- hmap_remove(all_bfds, &bfd->node);
- netdev_close(bfd->netdev);
- free(bfd->name);
- free(bfd);
- ovs_mutex_unlock(&mutex);
- }
+ if (bfd && ovs_refcount_unref(&bfd->ref_cnt) == 1) {
+ ovs_mutex_lock(&mutex);
+ hmap_remove(all_bfds, &bfd->node);
+ netdev_close(bfd->netdev);
+ free(bfd->name);
+ free(bfd);
+ ovs_mutex_unlock(&mutex);
}
}
ip->ip_ttl = MAXTTL;
ip->ip_tos = IPTOS_LOWDELAY | IPTOS_THROUGHPUT;
ip->ip_proto = IPPROTO_UDP;
- /* Use link local addresses: */
- put_16aligned_be32(&ip->ip_src, htonl(0xA9FE0100)); /* 169.254.1.0. */
- put_16aligned_be32(&ip->ip_dst, htonl(0xA9FE0101)); /* 169.254.1.1. */
+ put_16aligned_be32(&ip->ip_src, bfd->ip_src);
+ put_16aligned_be32(&ip->ip_dst, bfd->ip_dst);
ip->ip_csum = csum(ip, sizeof *ip);
udp = ofpbuf_put_zeros(p, sizeof *udp);
enum flags flags;
uint8_t version;
struct msg *msg;
+ const uint8_t *l7 = ofpbuf_get_udp_payload(p);
+
+ if (!l7) {
+ return; /* No UDP payload. */
+ }
/* This function is designed to follow section RFC 5880 6.8.6 closely. */
goto out;
}
- msg = ofpbuf_at(p, (uint8_t *)p->l7 - (uint8_t *)p->data, BFD_PACKET_LEN);
+ msg = ofpbuf_at(p, l7 - (uint8_t *)ofpbuf_data(p), BFD_PACKET_LEN);
if (!msg) {
VLOG_INFO_RL(&rl, "%s: Received too-short BFD control message (only "
"%"PRIdPTR" bytes long, at least %d required).",
- bfd->name, (uint8_t *) ofpbuf_tail(p) - (uint8_t *) p->l7,
+ bfd->name, (uint8_t *) ofpbuf_tail(p) - l7,
BFD_PACKET_LEN);
goto out;
}
goto out;
}
+ if (bfd->rmt_state != rmt_state) {
+ bfd_status_changed(bfd);
+ }
+
bfd->rmt_disc = ntohl(msg->my_disc);
bfd->rmt_state = rmt_state;
bfd->rmt_flags = flags;
rmt_min_rx = MAX(ntohl(msg->min_rx) / 1000, 1);
if (bfd->rmt_min_rx != rmt_min_rx) {
bfd->rmt_min_rx = rmt_min_rx;
- bfd_set_next_tx(bfd);
+ if (bfd->next_tx) {
+ bfd_set_next_tx(bfd);
+ }
log_msg(VLL_INFO, msg, "New remote min_rx", bfd);
}
&& bfd->rmt_diag != DIAG_RCPATH_DOWN;
if (bfd->last_forwarding != last_forwarding) {
bfd->flap_count++;
- seq_change(connectivity_seq_get());
+ bfd_status_changed(bfd);
}
return bfd->last_forwarding;
}
/* Helpers. */
+static bool
+bfd_lookup_ip(const char *host_name, struct in_addr *addr)
+{
+ if (!inet_pton(AF_INET, host_name, addr)) {
+ VLOG_ERR_RL(&rl, "\"%s\" is not a valid IP address", host_name);
+ return false;
+ }
+ return true;
+}
+
static bool
bfd_in_poll(const struct bfd *bfd) OVS_REQUIRES(mutex)
{
bfd_set_state(struct bfd *bfd, enum state state, enum diag diag)
OVS_REQUIRES(mutex)
{
- if (diag == DIAG_NONE && bfd->cpath_down) {
+ if (bfd->cpath_down) {
diag = DIAG_CPATH_DOWN;
}
bfd_decay_update(bfd);
}
- seq_change(connectivity_seq_get());
+ bfd_status_changed(bfd);
}
}
bfd->decay_detect_time = MAX(bfd->decay_min_rx, 2000) + time_msec();
}
+/* Records the status change and changes the global connectivity seq. */
+static void
+bfd_status_changed(struct bfd *bfd) OVS_REQUIRES(mutex)
+{
+ seq_change(connectivity_seq_get());
+ bfd->status_changed = true;
+}
+
static void
bfd_forwarding_if_rx_update(struct bfd *bfd) OVS_REQUIRES(mutex)
{
goto out;
}
bfd->forwarding_override = forwarding_override;
+ bfd_status_changed(bfd);
} else {
HMAP_FOR_EACH (bfd, node, all_bfds) {
bfd->forwarding_override = forwarding_override;
+ bfd_status_changed(bfd);
}
}