mirror of
https://github.com/openvswitch/ovs
synced 2025-09-01 14:55:18 +00:00
learning-switch: Break packet-in processing into two steps.
This commit is contained in:
@@ -392,12 +392,57 @@ process_switch_features(struct lswitch *sw, struct rconn *rconn, void *osf_)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint16_t
|
||||||
|
lswitch_choose_destination(struct lswitch *sw, const flow_t *flow)
|
||||||
|
{
|
||||||
|
uint16_t out_port;
|
||||||
|
|
||||||
|
/* Learn the source MAC. */
|
||||||
|
if (may_learn(sw, flow->in_port) && sw->ml) {
|
||||||
|
if (mac_learning_learn(sw->ml, flow->dl_src, 0, flow->in_port,
|
||||||
|
GRAT_ARP_LOCK_NONE)) {
|
||||||
|
VLOG_DBG_RL(&rl, "%016llx: learned that "ETH_ADDR_FMT" is on "
|
||||||
|
"port %"PRIu16, sw->datapath_id,
|
||||||
|
ETH_ADDR_ARGS(flow->dl_src), flow->in_port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Drop frames for reserved multicast addresses. */
|
||||||
|
if (eth_addr_is_reserved(flow->dl_dst)) {
|
||||||
|
return OFPP_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!may_recv(sw, flow->in_port, false)) {
|
||||||
|
/* STP prevents receiving anything on this port. */
|
||||||
|
return OFPP_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
out_port = OFPP_FLOOD;
|
||||||
|
if (sw->ml) {
|
||||||
|
int learned_port = mac_learning_lookup(sw->ml, flow->dl_dst, 0, NULL);
|
||||||
|
if (learned_port >= 0 && may_send(sw, learned_port)) {
|
||||||
|
out_port = learned_port;
|
||||||
|
if (out_port == flow->in_port) {
|
||||||
|
/* Don't send a packet back out its input port. */
|
||||||
|
return OFPP_NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if we need to use "NORMAL" action. */
|
||||||
|
if (sw->action_normal && out_port != OFPP_FLOOD) {
|
||||||
|
return OFPP_NORMAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return out_port;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
process_packet_in(struct lswitch *sw, struct rconn *rconn, void *opi_)
|
process_packet_in(struct lswitch *sw, struct rconn *rconn, void *opi_)
|
||||||
{
|
{
|
||||||
struct ofp_packet_in *opi = opi_;
|
struct ofp_packet_in *opi = opi_;
|
||||||
uint16_t in_port = ntohs(opi->in_port);
|
uint16_t in_port = ntohs(opi->in_port);
|
||||||
uint16_t out_port = OFPP_FLOOD;
|
uint16_t out_port;
|
||||||
|
|
||||||
size_t pkt_ofs, pkt_len;
|
size_t pkt_ofs, pkt_len;
|
||||||
struct ofpbuf pkt;
|
struct ofpbuf pkt;
|
||||||
@@ -410,44 +455,14 @@ process_packet_in(struct lswitch *sw, struct rconn *rconn, void *opi_)
|
|||||||
pkt.size = pkt_len;
|
pkt.size = pkt_len;
|
||||||
flow_extract(&pkt, 0, in_port, &flow);
|
flow_extract(&pkt, 0, in_port, &flow);
|
||||||
|
|
||||||
if (may_learn(sw, in_port) && sw->ml) {
|
/* Choose output port. */
|
||||||
if (mac_learning_learn(sw->ml, flow.dl_src, 0, in_port,
|
out_port = lswitch_choose_destination(sw, &flow);
|
||||||
GRAT_ARP_LOCK_NONE)) {
|
|
||||||
VLOG_DBG_RL(&rl, "%016llx: learned that "ETH_ADDR_FMT" is on "
|
|
||||||
"port %"PRIu16, sw->datapath_id,
|
|
||||||
ETH_ADDR_ARGS(flow.dl_src), in_port);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Drop frames for reserved multicast addresses. */
|
/* Send the packet, and possibly the whole flow, to the output port. */
|
||||||
if (eth_addr_is_reserved(flow.dl_dst)) {
|
if (sw->max_idle >= 0 && (!sw->ml || out_port != OFPP_FLOOD)) {
|
||||||
goto drop_it;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!may_recv(sw, in_port, false)) {
|
|
||||||
/* STP prevents receiving anything on this port. */
|
|
||||||
goto drop_it;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sw->ml) {
|
|
||||||
int learned_port = mac_learning_lookup(sw->ml, flow.dl_dst, 0, NULL);
|
|
||||||
if (learned_port >= 0 && may_send(sw, learned_port)) {
|
|
||||||
out_port = learned_port;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (in_port == out_port) {
|
|
||||||
/* Don't send out packets on their input ports. */
|
|
||||||
goto drop_it;
|
|
||||||
} else if (sw->max_idle >= 0 && (!sw->ml || out_port != OFPP_FLOOD)) {
|
|
||||||
struct ofpbuf *buffer;
|
struct ofpbuf *buffer;
|
||||||
struct ofp_flow_mod *ofm;
|
struct ofp_flow_mod *ofm;
|
||||||
|
|
||||||
/* Check if we need to use "NORMAL" action. */
|
|
||||||
if (sw->action_normal && out_port != OFPP_FLOOD) {
|
|
||||||
out_port = OFPP_NORMAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The output port is known, or we always flood everything, so add a
|
/* The output port is known, or we always flood everything, so add a
|
||||||
* new flow. */
|
* new flow. */
|
||||||
buffer = make_add_simple_flow(&flow, ntohl(opi->buffer_id),
|
buffer = make_add_simple_flow(&flow, ntohl(opi->buffer_id),
|
||||||
@@ -457,41 +472,24 @@ process_packet_in(struct lswitch *sw, struct rconn *rconn, void *opi_)
|
|||||||
queue_tx(sw, rconn, buffer);
|
queue_tx(sw, rconn, buffer);
|
||||||
|
|
||||||
/* If the switch didn't buffer the packet, we need to send a copy. */
|
/* If the switch didn't buffer the packet, we need to send a copy. */
|
||||||
if (ntohl(opi->buffer_id) == UINT32_MAX) {
|
if (ntohl(opi->buffer_id) == UINT32_MAX && out_port != OFPP_NONE) {
|
||||||
queue_tx(sw, rconn,
|
queue_tx(sw, rconn,
|
||||||
make_unbuffered_packet_out(&pkt, in_port, out_port));
|
make_unbuffered_packet_out(&pkt, in_port, out_port));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
struct ofpbuf *b;
|
|
||||||
|
|
||||||
/* Check if we need to use "NORMAL" action. */
|
|
||||||
if (sw->action_normal && out_port != OFPP_FLOOD) {
|
|
||||||
out_port = OFPP_NORMAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We don't know that MAC, or we don't set up flows. Send along the
|
/* We don't know that MAC, or we don't set up flows. Send along the
|
||||||
* packet without setting up a flow. */
|
* packet without setting up a flow. */
|
||||||
if (ntohl(opi->buffer_id) == UINT32_MAX) {
|
if (ntohl(opi->buffer_id) == UINT32_MAX) {
|
||||||
b = make_unbuffered_packet_out(&pkt, in_port, out_port);
|
if (out_port != OFPP_NONE) {
|
||||||
|
queue_tx(sw, rconn,
|
||||||
|
make_unbuffered_packet_out(&pkt, in_port, out_port));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
b = make_buffered_packet_out(ntohl(opi->buffer_id),
|
queue_tx(sw, rconn,
|
||||||
in_port, out_port);
|
make_buffered_packet_out(ntohl(opi->buffer_id),
|
||||||
|
in_port, out_port));
|
||||||
}
|
}
|
||||||
queue_tx(sw, rconn, b);
|
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
|
|
||||||
drop_it:
|
|
||||||
if (sw->max_idle >= 0) {
|
|
||||||
/* Set up a flow to drop packets. */
|
|
||||||
queue_tx(sw, rconn, make_add_flow(&flow, ntohl(opi->buffer_id),
|
|
||||||
sw->max_idle, 0));
|
|
||||||
} else {
|
|
||||||
/* Just drop the packet, since we don't set up flows at all.
|
|
||||||
* XXX we should send a packet_out with no actions if buffer_id !=
|
|
||||||
* UINT32_MAX, to avoid clogging the kernel buffers. */
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@@ -186,14 +186,19 @@ make_add_simple_flow(const flow_t *flow,
|
|||||||
uint32_t buffer_id, uint16_t out_port,
|
uint32_t buffer_id, uint16_t out_port,
|
||||||
uint16_t idle_timeout)
|
uint16_t idle_timeout)
|
||||||
{
|
{
|
||||||
struct ofp_action_output *oao;
|
if (out_port != OFPP_NONE) {
|
||||||
struct ofpbuf *buffer = make_add_flow(flow, buffer_id, idle_timeout,
|
struct ofp_action_output *oao;
|
||||||
sizeof *oao);
|
struct ofpbuf *buffer;
|
||||||
oao = ofpbuf_put_zeros(buffer, sizeof *oao);
|
|
||||||
oao->type = htons(OFPAT_OUTPUT);
|
buffer = make_add_flow(flow, buffer_id, idle_timeout, sizeof *oao);
|
||||||
oao->len = htons(sizeof *oao);
|
oao = ofpbuf_put_zeros(buffer, sizeof *oao);
|
||||||
oao->port = htons(out_port);
|
oao->type = htons(OFPAT_OUTPUT);
|
||||||
return buffer;
|
oao->len = htons(sizeof *oao);
|
||||||
|
oao->port = htons(out_port);
|
||||||
|
return buffer;
|
||||||
|
} else {
|
||||||
|
return make_add_flow(flow, buffer_id, idle_timeout, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ofpbuf *
|
struct ofpbuf *
|
||||||
@@ -259,12 +264,16 @@ struct ofpbuf *
|
|||||||
make_buffered_packet_out(uint32_t buffer_id,
|
make_buffered_packet_out(uint32_t buffer_id,
|
||||||
uint16_t in_port, uint16_t out_port)
|
uint16_t in_port, uint16_t out_port)
|
||||||
{
|
{
|
||||||
struct ofp_action_output action;
|
if (out_port != OFPP_NONE) {
|
||||||
action.type = htons(OFPAT_OUTPUT);
|
struct ofp_action_output action;
|
||||||
action.len = htons(sizeof action);
|
action.type = htons(OFPAT_OUTPUT);
|
||||||
action.port = htons(out_port);
|
action.len = htons(sizeof action);
|
||||||
return make_packet_out(NULL, buffer_id, in_port,
|
action.port = htons(out_port);
|
||||||
(struct ofp_action_header *) &action, 1);
|
return make_packet_out(NULL, buffer_id, in_port,
|
||||||
|
(struct ofp_action_header *) &action, 1);
|
||||||
|
} else {
|
||||||
|
return make_packet_out(NULL, buffer_id, in_port, NULL, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Creates and returns an OFPT_ECHO_REQUEST message with an empty payload. */
|
/* Creates and returns an OFPT_ECHO_REQUEST message with an empty payload. */
|
||||||
|
Reference in New Issue
Block a user