Skip to content
Snippets Groups Projects
Commit c25b98f6 authored by Ryan Izard's avatar Ryan Izard
Browse files

refactor some of Forwarding's doForwardFlow to make it easier to understand

parent ea5d75bf
No related branches found
No related tags found
No related merge requests found
......@@ -94,10 +94,10 @@ import org.slf4j.LoggerFactory;
public class Forwarding extends ForwardingBase implements IFloodlightModule, IOFSwitchListener, ILinkDiscoveryListener, IRoutingDecisionChangedListener {
protected static Logger log = LoggerFactory.getLogger(Forwarding.class);
final static U64 DEFAULT_FORWARDING_COOKIE = AppCookie.makeCookie(FORWARDING_APP_ID, 0);
// This mask determines how much of each cookie will contain IRoutingDecision descriptor bits.
private final long DECISION_MASK = 0x00000000ffffffffL; // TODO: shrink this mask if you need to add more sub-fields.
private final long DECISION_MASK = 0x00000000ffffffffL; // TODO: shrink this mask if you need to add more sub-fields.
@Override
public Command processPacketInMessage(IOFSwitch sw, OFPacketIn pi, IRoutingDecision decision, FloodlightContext cntx) {
Ethernet eth = IFloodlightProviderService.bcStore.get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD);
......@@ -140,108 +140,108 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule, IOF
return Command.CONTINUE;
}
/**
* Builds a cookie that includes routing decision information.
*
* @param decision The routing decision providing a descriptor, or null
* @return A cookie with our app id and the required routing fields masked-in
*/
protected U64 makeForwardingCookie(IRoutingDecision decision) {
int user_fields = 0;
U64 decision_cookie = (decision == null) ? null : decision.getDescriptor();
if (decision_cookie != null) {
user_fields |= AppCookie.extractUser(decision_cookie) & DECISION_MASK;
}
// TODO: Mask in any other required fields here (e.g. link failure flowset ID)
if (user_fields == 0) {
return DEFAULT_FORWARDING_COOKIE;
}
return AppCookie.makeCookie(FORWARDING_APP_ID, user_fields);
}
/** Called when the handleDecisionChange is triggered by an event (routing decision was changed in firewall).
*
* @param changedDecisions Masked routing descriptors for flows that should be deleted from the switch.
*/
@Override
public void routingDecisionChanged(Iterable<Masked<U64>> changedDecisions) {
deleteFlowsByDescriptor(changedDecisions);
}
/**
* Converts a sequence of masked IRoutingDecision descriptors into masked Forwarding cookies.
*
* This generates a list of masked cookies that can then be matched in flow-mod messages.
*
* @param maskedDescriptors A sequence of masked cookies describing IRoutingDecision descriptors
* @return A collection of masked cookies suitable for flow-mod operations
*/
protected Collection<Masked<U64>> convertRoutingDecisionDescriptors(Iterable<Masked<U64>> maskedDescriptors) {
if(maskedDescriptors == null) {
return null;
}
ImmutableList.Builder<Masked<U64>> resultBuilder = ImmutableList.builder();
for (Masked<U64> maskedDescriptor : maskedDescriptors) {
long user_mask = AppCookie.extractUser(maskedDescriptor.getMask()) & DECISION_MASK;
long user_value = AppCookie.extractUser(maskedDescriptor.getValue()) & user_mask;
// TODO combine in any other cookie fields you need here.
resultBuilder.add(
Masked.of(
AppCookie.makeCookie(FORWARDING_APP_ID, (int)user_value),
AppCookie.getAppFieldMask().or(U64.of(user_mask))
)
);
}
return resultBuilder.build();
}
/**
* Builds a cookie that includes routing decision information.
*
* @param decision The routing decision providing a descriptor, or null
* @return A cookie with our app id and the required routing fields masked-in
*/
protected U64 makeForwardingCookie(IRoutingDecision decision) {
int user_fields = 0;
U64 decision_cookie = (decision == null) ? null : decision.getDescriptor();
if (decision_cookie != null) {
user_fields |= AppCookie.extractUser(decision_cookie) & DECISION_MASK;
}
// TODO: Mask in any other required fields here (e.g. link failure flowset ID)
if (user_fields == 0) {
return DEFAULT_FORWARDING_COOKIE;
}
return AppCookie.makeCookie(FORWARDING_APP_ID, user_fields);
}
/** Called when the handleDecisionChange is triggered by an event (routing decision was changed in firewall).
*
* @param changedDecisions Masked routing descriptors for flows that should be deleted from the switch.
*/
@Override
public void routingDecisionChanged(Iterable<Masked<U64>> changedDecisions) {
deleteFlowsByDescriptor(changedDecisions);
}
/**
* Converts a sequence of masked IRoutingDecision descriptors into masked Forwarding cookies.
*
* This generates a list of masked cookies that can then be matched in flow-mod messages.
*
* @param maskedDescriptors A sequence of masked cookies describing IRoutingDecision descriptors
* @return A collection of masked cookies suitable for flow-mod operations
*/
protected Collection<Masked<U64>> convertRoutingDecisionDescriptors(Iterable<Masked<U64>> maskedDescriptors) {
if(maskedDescriptors == null) {
return null;
}
ImmutableList.Builder<Masked<U64>> resultBuilder = ImmutableList.builder();
for (Masked<U64> maskedDescriptor : maskedDescriptors) {
long user_mask = AppCookie.extractUser(maskedDescriptor.getMask()) & DECISION_MASK;
long user_value = AppCookie.extractUser(maskedDescriptor.getValue()) & user_mask;
// TODO combine in any other cookie fields you need here.
resultBuilder.add(
Masked.of(
AppCookie.makeCookie(FORWARDING_APP_ID, (int)user_value),
AppCookie.getAppFieldMask().or(U64.of(user_mask))
)
);
}
return resultBuilder.build();
}
/**
* On all active switches, deletes all flows matching the IRoutingDecision descriptors provided
* as arguments.
*
* @param descriptors The descriptors and masks describing which flows to delete.
*/
protected void deleteFlowsByDescriptor(Iterable<Masked<U64>> descriptors) {
Collection<Masked<U64>> masked_cookies = convertRoutingDecisionDescriptors(descriptors);
if (masked_cookies != null && !masked_cookies.isEmpty()) {
Map<OFVersion, List<OFMessage>> cache = Maps.newHashMap();
for (DatapathId dpid : switchService.getAllSwitchDpids()) {
IOFSwitch sw = switchService.getActiveSwitch(dpid);
if (sw == null) {
continue;
}
OFVersion ver = sw.getOFFactory().getVersion();
if (cache.containsKey(ver)) {
sw.write(cache.get(ver));
} else {
ImmutableList.Builder<OFMessage> msgsBuilder = ImmutableList.builder();
for (Masked<U64> masked_cookie : masked_cookies) {
msgsBuilder.add(
sw.getOFFactory().buildFlowDelete()
.setCookie(masked_cookie.getValue())
.setCookieMask(masked_cookie.getMask())
.build()
);
}
List<OFMessage> msgs = msgsBuilder.build();
sw.write(msgs);
cache.put(ver, msgs);
}
}
}
}
* On all active switches, deletes all flows matching the IRoutingDecision descriptors provided
* as arguments.
*
* @param descriptors The descriptors and masks describing which flows to delete.
*/
protected void deleteFlowsByDescriptor(Iterable<Masked<U64>> descriptors) {
Collection<Masked<U64>> masked_cookies = convertRoutingDecisionDescriptors(descriptors);
if (masked_cookies != null && !masked_cookies.isEmpty()) {
Map<OFVersion, List<OFMessage>> cache = Maps.newHashMap();
for (DatapathId dpid : switchService.getAllSwitchDpids()) {
IOFSwitch sw = switchService.getActiveSwitch(dpid);
if (sw == null) {
continue;
}
OFVersion ver = sw.getOFFactory().getVersion();
if (cache.containsKey(ver)) {
sw.write(cache.get(ver));
} else {
ImmutableList.Builder<OFMessage> msgsBuilder = ImmutableList.builder();
for (Masked<U64> masked_cookie : masked_cookies) {
msgsBuilder.add(
sw.getOFFactory().buildFlowDelete()
.setCookie(masked_cookie.getValue())
.setCookieMask(masked_cookie.getMask())
.build()
);
}
List<OFMessage> msgs = msgsBuilder.build();
sw.write(msgs);
cache.put(ver, msgs);
}
}
}
}
protected void doDropFlow(IOFSwitch sw, OFPacketIn pi, IRoutingDecision decision, FloodlightContext cntx) {
......@@ -274,120 +274,111 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule, IOF
}
protected void doForwardFlow(IOFSwitch sw, OFPacketIn pi, IRoutingDecision decision, FloodlightContext cntx, boolean requestFlowRemovedNotifn) {
OFPort inPort = OFMessageUtils.getInPort(pi);
OFPort srcPort = OFMessageUtils.getInPort(pi);
DatapathId srcSw = sw.getId();
IDevice dstDevice = IDeviceService.fcStore.get(cntx, IDeviceService.CONTEXT_DST_DEVICE);
DatapathId source = sw.getId();
if (dstDevice != null) {
IDevice srcDevice = IDeviceService.fcStore.get(cntx, IDeviceService.CONTEXT_SRC_DEVICE);
if (srcDevice == null) {
log.error("No device entry found for source device. Is the device manager running? If so, report bug.");
return;
}
if (FLOOD_ALL_ARP_PACKETS &&
IFloodlightProviderService.bcStore.get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD).getEtherType()
== EthType.ARP) {
log.debug("ARP flows disabled in Forwarding. Flooding ARP packet");
doFlood(sw, pi, decision, cntx);
return;
}
IDevice srcDevice = IDeviceService.fcStore.get(cntx, IDeviceService.CONTEXT_SRC_DEVICE);
/* Validate that the source and destination are not on the same switch port */
boolean on_same_if = false;
for (SwitchPort dstDap : dstDevice.getAttachmentPoints()) {
if (sw.getId().equals(dstDap.getNodeId()) && inPort.equals(dstDap.getPortId())) {
on_same_if = true;
break;
}
}
if (dstDevice == null) {
log.debug("Destination device unknown. Flooding packet");
doFlood(sw, pi, decision, cntx);
return;
}
if (srcDevice == null) {
log.error("No device entry found for source device. Is the device manager running? If so, report bug.");
return;
}
if (on_same_if) {
log.info("Both source and destination are on the same switch/port {}/{}. Action = NOP", sw.toString(), inPort);
return;
/* Some physical switches partially support or do not support ARP flows */
if (FLOOD_ALL_ARP_PACKETS &&
IFloodlightProviderService.bcStore.get(cntx, IFloodlightProviderService.CONTEXT_PI_PAYLOAD).getEtherType()
== EthType.ARP) {
log.debug("ARP flows disabled in Forwarding. Flooding ARP packet");
doFlood(sw, pi, decision, cntx);
return;
}
/* This packet-in is from a switch in the path before its flow was installed along the path */
if (!topologyService.isEdge(srcSw, srcPort)) {
log.debug("Packet destination is known, but packet was not received on an edge port (rx on {}/{}). Flooding packet", srcSw, srcPort);
doFlood(sw, pi, decision, cntx);
return;
}
/*
* Search for the true attachment point. The true AP is
* not an endpoint of a link. It is a switch port w/o an
* associated link. Note this does not necessarily hold
* true for devices that 'live' between OpenFlow islands.
*
* TODO Account for the case where a device is actually
* attached between islands (possibly on a non-OF switch
* in between two OpenFlow switches).
*/
SwitchPort dstAp = null;
for (SwitchPort ap : dstDevice.getAttachmentPoints()) {
if (topologyService.isEdge(ap.getNodeId(), ap.getPortId())) {
dstAp = ap;
break;
}
}
/*
* This should only happen (perhaps) when the controller is
* actively learning a new topology and hasn't discovered
* all links yet, or a switch was in standalone mode and the
* packet in question was captured in flight on the dst point
* of a link.
*/
if (dstAp == null) {
log.debug("Could not locate edge attachment point for destination device {}. Flooding packet");
doFlood(sw, pi, decision, cntx);
return;
}
SwitchPort[] dstDaps = dstDevice.getAttachmentPoints();
SwitchPort dstDap = null;
/*
* Search for the true attachment point. The true AP is
* not an endpoint of a link. It is a switch port w/o an
* associated link. Note this does not necessarily hold
* true for devices that 'live' between OpenFlow islands.
*
* TODO Account for the case where a device is actually
* attached between islands (possibly on a non-OF switch
* in between two OpenFlow switches).
*/
for (SwitchPort ap : dstDaps) {
if (topologyService.isEdge(ap.getNodeId(), ap.getPortId())) {
dstDap = ap;
break;
}
}
/*
* This should only happen (perhaps) when the controller is
* actively learning a new topology and hasn't discovered
* all links yet, or a switch was in standalone mode and the
* packet in question was captured in flight on the dst point
* of a link.
*/
if (dstDap == null) {
log.debug("Could not locate edge attachment point for device {}. Flooding packet");
doFlood(sw, pi, decision, cntx);
return;
/* Validate that the source and destination are not on the same switch port */
if (sw.getId().equals(dstAp.getNodeId()) && srcPort.equals(dstAp.getPortId())) {
log.info("Both source and destination are on the same switch/port {}/{}. Dropping packet", sw.toString(), srcPort);
return;
}
U64 cookie = makeForwardingCookie(decision);
Path path = routingEngineService.getPath(srcSw,
srcPort,
dstAp.getNodeId(),
dstAp.getPortId());
Match m = createMatchFromPacket(sw, srcPort, cntx);
if (path != null) {
if (log.isDebugEnabled()) {
log.debug("pushRoute inPort={} route={} " +
"destination={}:{}",
new Object[] { srcPort, path,
dstAp.getNodeId(),
dstAp.getPortId()});
log.debug("Creating flow rules on the route, match rule: {}", m);
}
/* It's possible that we learned packed destination while it was in flight */
if (!topologyService.isEdge(source, inPort)) {
log.debug("Packet destination is known, but packet was not received on an edge port (rx on {}/{}). Flooding packet", source, inPort);
doFlood(sw, pi, decision, cntx);
return;
}
U64 cookie = makeForwardingCookie(decision);
Path path = routingEngineService.getPath(source,
inPort,
dstDap.getNodeId(),
dstDap.getPortId());
Match m = createMatchFromPacket(sw, inPort, cntx);
if (path != null) {
if (log.isDebugEnabled()) {
log.debug("pushRoute inPort={} route={} " +
"destination={}:{}",
new Object[] { inPort, path,
dstDap.getNodeId(),
dstDap.getPortId()});
}
log.debug("Cretaing flow rules on the route, match rule: {}", m);
pushRoute(path, m, pi, sw.getId(), cookie,
cntx, requestFlowRemovedNotifn,
OFFlowModCommand.ADD);
} else {
/* Route traverses no links --> src/dst devices on same switch */
log.debug("Could not compute route. Devices should be on same switch src={} and dst={}", srcDevice, dstDevice);
Path p = new Path(srcDevice.getAttachmentPoints()[0].getNodeId(), dstDevice.getAttachmentPoints()[0].getNodeId());
List<NodePortTuple> npts = new ArrayList<NodePortTuple>(2);
npts.add(new NodePortTuple(srcDevice.getAttachmentPoints()[0].getNodeId(),
srcDevice.getAttachmentPoints()[0].getPortId()));
npts.add(new NodePortTuple(dstDevice.getAttachmentPoints()[0].getNodeId(),
dstDevice.getAttachmentPoints()[0].getPortId()));
p.setPath(npts);
pushRoute(p, m, pi, sw.getId(), cookie,
cntx, requestFlowRemovedNotifn,
OFFlowModCommand.ADD);
}
pushRoute(path, m, pi, sw.getId(), cookie,
cntx, requestFlowRemovedNotifn,
OFFlowModCommand.ADD);
} else {
log.debug("Destination unknown. Flooding packet");
doFlood(sw, pi, decision, cntx);
/* Route traverses no links --> src/dst devices on same switch */
log.debug("Could not compute route. Devices should be on same switch src={} and dst={}", srcDevice, dstDevice);
Path p = new Path(srcDevice.getAttachmentPoints()[0].getNodeId(), dstDevice.getAttachmentPoints()[0].getNodeId());
List<NodePortTuple> npts = new ArrayList<NodePortTuple>(2);
npts.add(new NodePortTuple(srcDevice.getAttachmentPoints()[0].getNodeId(),
srcDevice.getAttachmentPoints()[0].getPortId()));
npts.add(new NodePortTuple(dstDevice.getAttachmentPoints()[0].getNodeId(),
dstDevice.getAttachmentPoints()[0].getPortId()));
p.setPath(npts);
pushRoute(p, m, pi, sw.getId(), cookie,
cntx, requestFlowRemovedNotifn,
OFFlowModCommand.ADD);
}
}
/**
......@@ -658,7 +649,7 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule, IOF
+ ", MAC=" + FLOWMOD_DEFAULT_MATCH_MAC
+ ", IP=" + FLOWMOD_DEFAULT_MATCH_IP
+ ", TPPT=" + FLOWMOD_DEFAULT_MATCH_TRANSPORT);
tmp = configParameters.get("detailed-match");
if (tmp != null) {
tmp = tmp.toLowerCase();
......@@ -681,7 +672,7 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule, IOF
+ ", DST_IP=" + FLOWMOD_DEFAULT_MATCH_IP_DST
+ ", SRC_TPPT=" + FLOWMOD_DEFAULT_MATCH_TRANSPORT_SRC
+ ", DST_TPPT=" + FLOWMOD_DEFAULT_MATCH_TRANSPORT_DST);
tmp = configParameters.get("flood-arp");
if (tmp != null) {
tmp = tmp.toLowerCase();
......@@ -788,16 +779,16 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule, IOF
if (srcSw != null) {
/* flows matching on src port */
msgs.add(srcSw.getOFFactory().buildFlowDelete()
.setCookie(DEFAULT_FORWARDING_COOKIE)
.setCookieMask(AppCookie.getAppFieldMask())
.setCookie(DEFAULT_FORWARDING_COOKIE)
.setCookieMask(AppCookie.getAppFieldMask())
.setMatch(srcSw.getOFFactory().buildMatch()
.setExact(MatchField.IN_PORT, u.getSrcPort())
.build())
.build());
/* flows outputting to src port */
msgs.add(srcSw.getOFFactory().buildFlowDelete()
.setCookie(DEFAULT_FORWARDING_COOKIE)
.setCookieMask(AppCookie.getAppFieldMask())
.setCookie(DEFAULT_FORWARDING_COOKIE)
.setCookieMask(AppCookie.getAppFieldMask())
.setOutPort(u.getSrcPort())
.build());
messageDamper.write(srcSw, msgs);
......@@ -813,16 +804,16 @@ public class Forwarding extends ForwardingBase implements IFloodlightModule, IOF
/* flows matching on dst port */
msgs.clear();
msgs.add(dstSw.getOFFactory().buildFlowDelete()
.setCookie(DEFAULT_FORWARDING_COOKIE)
.setCookieMask(AppCookie.getAppFieldMask())
.setCookie(DEFAULT_FORWARDING_COOKIE)
.setCookieMask(AppCookie.getAppFieldMask())
.setMatch(dstSw.getOFFactory().buildMatch()
.setExact(MatchField.IN_PORT, u.getDstPort())
.build())
.build());
/* flows outputting to dst port */
msgs.add(dstSw.getOFFactory().buildFlowDelete()
.setCookie(DEFAULT_FORWARDING_COOKIE)
.setCookieMask(AppCookie.getAppFieldMask())
.setCookie(DEFAULT_FORWARDING_COOKIE)
.setCookieMask(AppCookie.getAppFieldMask())
.setOutPort(u.getDstPort())
.build());
messageDamper.write(dstSw, msgs);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment