Jonathan Hart
Committed by Gerrit Code Review

Implementation of the NeighbourResolutionService.

Change-Id: I41fc48578df3027ec71ee7369171c8988ee7a85e
...@@ -21,36 +21,54 @@ import org.onosproject.net.host.HostService; ...@@ -21,36 +21,54 @@ import org.onosproject.net.host.HostService;
21 21
22 import java.util.Set; 22 import java.util.Set;
23 23
24 +import static org.onlab.packet.VlanId.vlanId;
24 import static org.onosproject.net.HostId.hostId; 25 import static org.onosproject.net.HostId.hostId;
25 26
26 /** 27 /**
27 - * Default neighbour message handler which implements basic proxying on a flat 28 + * Default neighbour message handler which implements basic proxying on an
28 * L2 network (i.e. ProxyArp behaviour). 29 * L2 network (i.e. ProxyArp behaviour).
29 */ 30 */
30 public class DefaultNeighbourMessageHandler implements NeighbourMessageHandler { 31 public class DefaultNeighbourMessageHandler implements NeighbourMessageHandler {
31 @Override 32 @Override
32 public void handleMessage(NeighbourMessageContext context, HostService hostService) { 33 public void handleMessage(NeighbourMessageContext context, HostService hostService) {
33 - // See if we have the target host in the host store 34 + switch (context.type()) {
34 - Set<Host> hosts = hostService.getHostsByIp(context.target()); 35 + case REPLY:
36 + Host h = hostService.getHost(hostId(context.packet().getDestinationMAC(),
37 + vlanId(context.packet().getVlanID())));
35 38
36 - Host dst = null; 39 + if (h == null) {
37 - Host src = hostService.getHost(hostId(context.srcMac(), context.vlan())); 40 + context.flood();
41 + } else {
42 + context.proxy(h.location());
43 + }
44 + break;
45 + case REQUEST:
46 + // See if we have the target host in the host store
47 + Set<Host> hosts = hostService.getHostsByIp(context.target());
48 +
49 + Host dst = null;
50 + Host src = hostService.getHost(hostId(context.srcMac(), context.vlan()));
38 51
39 - for (Host host : hosts) { 52 + for (Host host : hosts) {
40 - if (host.vlan().equals(context.vlan())) { 53 + if (host.vlan().equals(context.vlan())) {
41 - dst = host; 54 + dst = host;
42 - break; 55 + break;
56 + }
57 + }
58 +
59 + if (src != null && dst != null) {
60 + // We know the target host so we can respond
61 + context.reply(dst.mac());
62 + return;
43 } 63 }
44 - }
45 64
46 - if (src != null && dst != null) { 65 + // The request couldn't be resolved.
47 - // We know the target host so we can respond 66 + // Flood the request on all ports except the incoming port.
48 - context.reply(dst.mac()); 67 + context.flood();
49 - return; 68 + break;
69 + default:
70 + break;
50 } 71 }
51 72
52 - // The request couldn't be resolved.
53 - // Flood the request on all ports except the incoming port.
54 - context.flood();
55 } 73 }
56 } 74 }
......
...@@ -24,8 +24,6 @@ import org.onlab.packet.VlanId; ...@@ -24,8 +24,6 @@ import org.onlab.packet.VlanId;
24 import org.onosproject.incubator.net.intf.Interface; 24 import org.onosproject.incubator.net.intf.Interface;
25 import org.onosproject.net.ConnectPoint; 25 import org.onosproject.net.ConnectPoint;
26 26
27 -import static com.google.common.base.Preconditions.checkState;
28 -
29 /** 27 /**
30 * Context of an incoming neighbor message (e.g. ARP, NDP). 28 * Context of an incoming neighbor message (e.g. ARP, NDP).
31 * 29 *
...@@ -34,156 +32,91 @@ import static com.google.common.base.Preconditions.checkState; ...@@ -34,156 +32,91 @@ import static com.google.common.base.Preconditions.checkState;
34 * response to the incoming message.</p> 32 * response to the incoming message.</p>
35 */ 33 */
36 @Beta 34 @Beta
37 -public class NeighbourMessageContext { 35 +public interface NeighbourMessageContext {
38 -
39 - private final NeighbourProtocol protocol;
40 - private final NeighbourMessageType type;
41 -
42 - private final IpAddress target;
43 - private final IpAddress sender;
44 -
45 - private final Ethernet eth;
46 - private final ConnectPoint inPort;
47 -
48 - private final NeighbourMessageActions actions;
49 -
50 - /**
51 - * Creates a new neighbour message context.
52 - *
53 - * @param actions actions
54 - * @param eth ethernet frame
55 - * @param inPort incoming port
56 - * @param protocol message protocol
57 - * @param type message type
58 - * @param target target IP address
59 - * @param sender sender IP address
60 - */
61 - public NeighbourMessageContext(NeighbourMessageActions actions,
62 - Ethernet eth, ConnectPoint inPort,
63 - NeighbourProtocol protocol, NeighbourMessageType type,
64 - IpAddress target, IpAddress sender) {
65 - this.actions = actions;
66 - this.eth = eth;
67 - this.inPort = inPort;
68 - this.protocol = protocol;
69 - this.type = type;
70 - this.target = target;
71 - this.sender = sender;
72 - }
73 -
74 /** 36 /**
75 * Gets the port where the packet came in to the network. 37 * Gets the port where the packet came in to the network.
76 * 38 *
77 * @return connect point 39 * @return connect point
78 */ 40 */
79 - public ConnectPoint inPort() { 41 + ConnectPoint inPort();
80 - return inPort;
81 - }
82 42
83 /** 43 /**
84 * Gets the full parsed representation of the packet. 44 * Gets the full parsed representation of the packet.
85 * 45 *
86 * @return ethernet header 46 * @return ethernet header
87 */ 47 */
88 - public Ethernet packet() { 48 + Ethernet packet();
89 - return eth;
90 - }
91 49
92 /** 50 /**
93 * Gets the protocol of the packet. 51 * Gets the protocol of the packet.
94 * 52 *
95 * @return protocol 53 * @return protocol
96 */ 54 */
97 - public NeighbourProtocol protocol() { 55 + NeighbourProtocol protocol();
98 - return protocol;
99 - }
100 56
101 /** 57 /**
102 * Gets the message type of the packet. 58 * Gets the message type of the packet.
103 * 59 *
104 * @return message type 60 * @return message type
105 */ 61 */
106 - public NeighbourMessageType type() { 62 + NeighbourMessageType type();
107 - return type;
108 - }
109 63
110 /** 64 /**
111 * Gets the vlan of the packet, if any. 65 * Gets the vlan of the packet, if any.
112 * 66 *
113 * @return vlan 67 * @return vlan
114 */ 68 */
115 - public VlanId vlan() { 69 + VlanId vlan();
116 - return VlanId.vlanId(eth.getVlanID());
117 - }
118 70
119 /** 71 /**
120 * Gets the source MAC address of the message. 72 * Gets the source MAC address of the message.
121 * 73 *
122 * @return source MAC address 74 * @return source MAC address
123 */ 75 */
124 - public MacAddress srcMac() { 76 + MacAddress srcMac();
125 - return MacAddress.valueOf(eth.getSourceMACAddress());
126 - }
127 77
128 /** 78 /**
129 * Gets the target IP address of the message. 79 * Gets the target IP address of the message.
130 * 80 *
131 * @return target IP address 81 * @return target IP address
132 */ 82 */
133 - public IpAddress target() { 83 + IpAddress target();
134 - return target;
135 - }
136 84
137 /** 85 /**
138 * Gets the source IP address of the message. 86 * Gets the source IP address of the message.
139 * 87 *
140 * @return source IP address 88 * @return source IP address
141 */ 89 */
142 - public IpAddress sender() { 90 + IpAddress sender();
143 - return sender;
144 - }
145 91
146 /** 92 /**
147 * Proxies the message to a given output port. 93 * Proxies the message to a given output port.
148 * 94 *
149 * @param outPort output port 95 * @param outPort output port
150 */ 96 */
151 - public void proxy(ConnectPoint outPort) { 97 + void proxy(ConnectPoint outPort);
152 - actions.proxy(this, outPort);
153 - }
154 98
155 /** 99 /**
156 * Proxies the message to a given interface. 100 * Proxies the message to a given interface.
157 * 101 *
158 * @param outIntf output interface 102 * @param outIntf output interface
159 */ 103 */
160 - public void proxy(Interface outIntf) { 104 + void proxy(Interface outIntf);
161 - actions.proxy(this, outIntf);
162 - }
163 105
164 /** 106 /**
165 * Replies to the request message with a given MAC address. 107 * Replies to the request message with a given MAC address.
166 * 108 *
167 * @param targetMac target MAC address 109 * @param targetMac target MAC address
168 */ 110 */
169 - public void reply(MacAddress targetMac) { 111 + void reply(MacAddress targetMac);
170 - checkState(type == NeighbourMessageType.REQUEST, "can only reply to requests");
171 -
172 - actions.reply(this, targetMac);
173 - }
174 112
175 /** 113 /**
176 * Floods the incoming message out all ports except the input port. 114 * Floods the incoming message out all ports except the input port.
177 */ 115 */
178 - public void flood() { 116 + void flood();
179 - actions.flood(this);
180 - }
181 117
182 /** 118 /**
183 * Drops the incoming message. 119 * Drops the incoming message.
184 */ 120 */
185 - public void drop() { 121 + void drop();
186 - actions.drop(this);
187 - }
188 -
189 } 122 }
......
...@@ -45,4 +45,20 @@ public interface NeighbourResolutionService { ...@@ -45,4 +45,20 @@ public interface NeighbourResolutionService {
45 */ 45 */
46 void registerNeighbourHandler(Interface intf, NeighbourMessageHandler handler); 46 void registerNeighbourHandler(Interface intf, NeighbourMessageHandler handler);
47 47
48 + /**
49 + * Unregisters a neighbour message handler that was assigned to a connect
50 + * point.
51 + *
52 + * @param connectPoint connect point
53 + * @param handler neighbour message handler
54 + */
55 + void unregisterNeighbourHandler(ConnectPoint connectPoint, NeighbourMessageHandler handler);
56 +
57 + /**
58 + * Unregisters a neighbour message handler that was assigned to an interface.
59 + *
60 + * @param intf interface
61 + * @param handler neighbour message handler
62 + */
63 + void unregisterNeighbourHandler(Interface intf, NeighbourMessageHandler handler);
48 } 64 }
......
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +package org.onosproject.incubator.net.neighbour.impl;
18 +
19 +import com.google.common.annotations.Beta;
20 +import org.onlab.packet.ARP;
21 +import org.onlab.packet.Ethernet;
22 +import org.onlab.packet.ICMP6;
23 +import org.onlab.packet.IPv6;
24 +import org.onlab.packet.Ip4Address;
25 +import org.onlab.packet.Ip6Address;
26 +import org.onlab.packet.IpAddress;
27 +import org.onlab.packet.MacAddress;
28 +import org.onlab.packet.VlanId;
29 +import org.onlab.packet.ndp.NeighborSolicitation;
30 +import org.onosproject.incubator.net.intf.Interface;
31 +import org.onosproject.incubator.net.neighbour.NeighbourMessageActions;
32 +import org.onosproject.incubator.net.neighbour.NeighbourMessageContext;
33 +import org.onosproject.incubator.net.neighbour.NeighbourMessageType;
34 +import org.onosproject.incubator.net.neighbour.NeighbourProtocol;
35 +import org.onosproject.net.ConnectPoint;
36 +
37 +import static com.google.common.base.Preconditions.checkState;
38 +
39 +/**
40 + * Default implementation of a neighbour message context.
41 + */
42 +@Beta
43 +public class DefaultNeighbourMessageContext implements NeighbourMessageContext {
44 +
45 + private final NeighbourProtocol protocol;
46 + private final NeighbourMessageType type;
47 +
48 + private final IpAddress target;
49 + private final IpAddress sender;
50 +
51 + private final Ethernet eth;
52 + private final ConnectPoint inPort;
53 +
54 + private final NeighbourMessageActions actions;
55 +
56 + /**
57 + * Creates a new neighbour message context.
58 + *
59 + * @param actions actions
60 + * @param eth ethernet frame
61 + * @param inPort incoming port
62 + * @param protocol message protocol
63 + * @param type message type
64 + * @param target target IP address
65 + * @param sender sender IP address
66 + */
67 + DefaultNeighbourMessageContext(NeighbourMessageActions actions,
68 + Ethernet eth, ConnectPoint inPort,
69 + NeighbourProtocol protocol,
70 + NeighbourMessageType type,
71 + IpAddress target, IpAddress sender) {
72 + this.actions = actions;
73 + this.eth = eth;
74 + this.inPort = inPort;
75 + this.protocol = protocol;
76 + this.type = type;
77 + this.target = target;
78 + this.sender = sender;
79 + }
80 +
81 + @Override
82 + public ConnectPoint inPort() {
83 + return inPort;
84 + }
85 +
86 + @Override
87 + public Ethernet packet() {
88 + return eth;
89 + }
90 +
91 + @Override
92 + public NeighbourProtocol protocol() {
93 + return protocol;
94 + }
95 +
96 + @Override
97 + public NeighbourMessageType type() {
98 + return type;
99 + }
100 +
101 + @Override
102 + public VlanId vlan() {
103 + return VlanId.vlanId(eth.getVlanID());
104 + }
105 +
106 + @Override
107 + public MacAddress srcMac() {
108 + return MacAddress.valueOf(eth.getSourceMACAddress());
109 + }
110 +
111 + @Override
112 + public IpAddress target() {
113 + return target;
114 + }
115 +
116 + @Override
117 + public IpAddress sender() {
118 + return sender;
119 + }
120 +
121 + @Override
122 + public void proxy(ConnectPoint outPort) {
123 + actions.proxy(this, outPort);
124 + }
125 +
126 + @Override
127 + public void proxy(Interface outIntf) {
128 + actions.proxy(this, outIntf);
129 + }
130 +
131 + @Override
132 + public void reply(MacAddress targetMac) {
133 + checkState(type == NeighbourMessageType.REQUEST, "can only reply to requests");
134 +
135 + actions.reply(this, targetMac);
136 + }
137 +
138 + @Override
139 + public void flood() {
140 + actions.flood(this);
141 + }
142 +
143 + @Override
144 + public void drop() {
145 + actions.drop(this);
146 + }
147 +
148 + /**
149 + * Attempts to create a MessageContext for the given Ethernet frame. If the
150 + * frame is a valid ARP or NDP request or response, a context will be
151 + * created.
152 + *
153 + * @param eth input Ethernet frame
154 + * @param inPort in port
155 + * @return MessageContext if the packet was ARP or NDP, otherwise null
156 + */
157 + public static NeighbourMessageContext createContext(Ethernet eth,
158 + ConnectPoint inPort,
159 + NeighbourMessageActions actions) {
160 + if (eth.getEtherType() == Ethernet.TYPE_ARP) {
161 + return createArpContext(eth, inPort, actions);
162 + } else if (eth.getEtherType() == Ethernet.TYPE_IPV6) {
163 + return createNdpContext(eth, inPort, actions);
164 + }
165 +
166 + return null;
167 + }
168 +
169 + /**
170 + * Extracts context information from ARP packets.
171 + *
172 + * @param eth input Ethernet frame that is thought to be ARP
173 + * @param inPort in port
174 + * @return MessageContext object if the packet was a valid ARP packet,
175 + * otherwise null
176 + */
177 + private static NeighbourMessageContext createArpContext(Ethernet eth,
178 + ConnectPoint inPort,
179 + NeighbourMessageActions actions) {
180 + if (eth.getEtherType() != Ethernet.TYPE_ARP) {
181 + return null;
182 + }
183 +
184 + ARP arp = (ARP) eth.getPayload();
185 +
186 + IpAddress target = Ip4Address.valueOf(arp.getTargetProtocolAddress());
187 + IpAddress sender = Ip4Address.valueOf(arp.getSenderProtocolAddress());
188 +
189 + NeighbourMessageType type;
190 + if (arp.getOpCode() == ARP.OP_REQUEST) {
191 + type = NeighbourMessageType.REQUEST;
192 + } else if (arp.getOpCode() == ARP.OP_REPLY) {
193 + type = NeighbourMessageType.REPLY;
194 + } else {
195 + return null;
196 + }
197 +
198 + return new DefaultNeighbourMessageContext(actions, eth, inPort,
199 + NeighbourProtocol.ARP, type, target, sender);
200 + }
201 +
202 + /**
203 + * Extracts context information from NDP packets.
204 + *
205 + * @param eth input Ethernet frame that is thought to be NDP
206 + * @param inPort in port
207 + * @return MessageContext object if the packet was a valid NDP packet,
208 + * otherwise null
209 + */
210 + private static NeighbourMessageContext createNdpContext(Ethernet eth,
211 + ConnectPoint inPort,
212 + NeighbourMessageActions actions) {
213 + if (eth.getEtherType() != Ethernet.TYPE_IPV6) {
214 + return null;
215 + }
216 + IPv6 ipv6 = (IPv6) eth.getPayload();
217 +
218 + if (ipv6.getNextHeader() != IPv6.PROTOCOL_ICMP6) {
219 + return null;
220 + }
221 + ICMP6 icmpv6 = (ICMP6) ipv6.getPayload();
222 +
223 + IpAddress sender = Ip6Address.valueOf(ipv6.getSourceAddress());
224 + IpAddress target = null;
225 +
226 + NeighbourMessageType type;
227 + if (icmpv6.getIcmpType() == ICMP6.NEIGHBOR_SOLICITATION) {
228 + type = NeighbourMessageType.REQUEST;
229 + NeighborSolicitation nsol = (NeighborSolicitation) icmpv6.getPayload();
230 + target = Ip6Address.valueOf(nsol.getTargetAddress());
231 + } else if (icmpv6.getIcmpType() == ICMP6.NEIGHBOR_ADVERTISEMENT) {
232 + type = NeighbourMessageType.REPLY;
233 + } else {
234 + return null;
235 + }
236 +
237 + return new DefaultNeighbourMessageContext(actions, eth, inPort,
238 + NeighbourProtocol.NDP, type, target, sender);
239 + }
240 +
241 +}
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +package org.onosproject.incubator.net.neighbour.impl;
18 +
19 +import com.google.common.collect.LinkedListMultimap;
20 +import com.google.common.collect.ListMultimap;
21 +import com.google.common.collect.Multimaps;
22 +import org.apache.felix.scr.annotations.Activate;
23 +import org.apache.felix.scr.annotations.Component;
24 +import org.apache.felix.scr.annotations.Deactivate;
25 +import org.apache.felix.scr.annotations.Modified;
26 +import org.apache.felix.scr.annotations.Property;
27 +import org.apache.felix.scr.annotations.Reference;
28 +import org.apache.felix.scr.annotations.ReferenceCardinality;
29 +import org.apache.felix.scr.annotations.Service;
30 +import org.onlab.packet.ARP;
31 +import org.onlab.packet.Ethernet;
32 +import org.onlab.packet.ICMP6;
33 +import org.onlab.packet.IPv6;
34 +import org.onlab.packet.Ip4Address;
35 +import org.onlab.packet.Ip6Address;
36 +import org.onlab.packet.MacAddress;
37 +import org.onlab.packet.VlanId;
38 +import org.onlab.packet.ndp.NeighborAdvertisement;
39 +import org.onlab.packet.ndp.NeighborDiscoveryOptions;
40 +import org.onlab.util.Tools;
41 +import org.onosproject.cfg.ComponentConfigService;
42 +import org.onosproject.core.ApplicationId;
43 +import org.onosproject.core.CoreService;
44 +import org.onosproject.incubator.net.intf.Interface;
45 +import org.onosproject.incubator.net.neighbour.NeighbourMessageActions;
46 +import org.onosproject.incubator.net.neighbour.NeighbourMessageContext;
47 +import org.onosproject.incubator.net.neighbour.NeighbourMessageHandler;
48 +import org.onosproject.incubator.net.neighbour.NeighbourResolutionService;
49 +import org.onosproject.net.ConnectPoint;
50 +import org.onosproject.net.edge.EdgePortService;
51 +import org.onosproject.net.flow.DefaultTrafficSelector;
52 +import org.onosproject.net.flow.DefaultTrafficTreatment;
53 +import org.onosproject.net.flow.TrafficSelector;
54 +import org.onosproject.net.flow.TrafficTreatment;
55 +import org.onosproject.net.host.HostService;
56 +import org.onosproject.net.packet.DefaultOutboundPacket;
57 +import org.onosproject.net.packet.InboundPacket;
58 +import org.onosproject.net.packet.PacketContext;
59 +import org.onosproject.net.packet.PacketProcessor;
60 +import org.onosproject.net.packet.PacketService;
61 +import org.osgi.service.component.ComponentContext;
62 +import org.slf4j.Logger;
63 +import org.slf4j.LoggerFactory;
64 +
65 +import java.nio.ByteBuffer;
66 +import java.util.Dictionary;
67 +import java.util.List;
68 +import java.util.Objects;
69 +
70 +import static com.google.common.base.Preconditions.checkNotNull;
71 +import static org.onlab.packet.Ethernet.TYPE_ARP;
72 +import static org.onlab.packet.Ethernet.TYPE_IPV6;
73 +import static org.onlab.packet.ICMP6.NEIGHBOR_ADVERTISEMENT;
74 +import static org.onlab.packet.ICMP6.NEIGHBOR_SOLICITATION;
75 +import static org.onlab.packet.IPv6.PROTOCOL_ICMP6;
76 +import static org.onosproject.net.packet.PacketPriority.CONTROL;
77 +
78 +/**
79 + * Manages handlers for neighbour messages.
80 + */
81 +@Service
82 +@Component(immediate = true)
83 +public class NeighbourPacketManager implements NeighbourResolutionService {
84 +
85 + private final Logger log = LoggerFactory.getLogger(getClass());
86 +
87 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
88 + protected CoreService coreService;
89 +
90 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
91 + protected HostService hostService;
92 +
93 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
94 + protected EdgePortService edgeService;
95 +
96 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
97 + protected PacketService packetService;
98 +
99 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
100 + protected ComponentConfigService componentConfigService;
101 +
102 + @Property(name = "ndpEnabled", boolValue = false,
103 + label = "Enable IPv6 neighbour discovery")
104 + protected boolean ndpEnabled = false;
105 +
106 + private static final String APP_NAME = "org.onosproject.neighbour";
107 + private ApplicationId appId;
108 +
109 + private ListMultimap<ConnectPoint, HandlerRegistration> packetHandlers =
110 + Multimaps.synchronizedListMultimap(LinkedListMultimap.create());
111 +
112 + private final InternalPacketProcessor processor = new InternalPacketProcessor();
113 + private final InternalNeighbourMessageActions actions = new InternalNeighbourMessageActions();
114 +
115 + @Activate
116 + protected void activate(ComponentContext context) {
117 + appId = coreService.registerApplication(APP_NAME);
118 +
119 + componentConfigService.registerProperties(getClass());
120 + modified(context);
121 +
122 + packetService.addProcessor(processor, PacketProcessor.director(1));
123 + }
124 +
125 + @Deactivate
126 + protected void deactivate() {
127 + cancelPackets();
128 + packetService.removeProcessor(processor);
129 + componentConfigService.unregisterProperties(getClass(), false);
130 + }
131 +
132 + @Modified
133 + protected void modified(ComponentContext context) {
134 + Dictionary<?, ?> properties = context.getProperties();
135 + Boolean flag;
136 +
137 + flag = Tools.isPropertyEnabled(properties, "ndpEnabled");
138 + if (flag != null) {
139 + ndpEnabled = flag;
140 + log.info("IPv6 neighbor discovery is {}",
141 + ndpEnabled ? "enabled" : "disabled");
142 + }
143 +
144 + requestPackets();
145 + }
146 +
147 + private void requestPackets() {
148 + packetService.requestPackets(buildArpSelector(), CONTROL, appId);
149 +
150 + if (ndpEnabled) {
151 + packetService.requestPackets(buildNeighborSolicitationSelector(),
152 + CONTROL, appId);
153 + packetService.requestPackets(buildNeighborAdvertisementSelector(),
154 + CONTROL, appId);
155 + } else {
156 + packetService.cancelPackets(buildNeighborSolicitationSelector(),
157 + CONTROL, appId);
158 + packetService.cancelPackets(buildNeighborAdvertisementSelector(),
159 + CONTROL, appId);
160 + }
161 + }
162 +
163 + private void cancelPackets() {
164 + packetService.cancelPackets(buildArpSelector(), CONTROL, appId);
165 + packetService.cancelPackets(buildNeighborSolicitationSelector(),
166 + CONTROL, appId);
167 + packetService.cancelPackets(buildNeighborAdvertisementSelector(),
168 + CONTROL, appId);
169 + }
170 +
171 + private TrafficSelector buildArpSelector() {
172 + return DefaultTrafficSelector.builder()
173 + .matchEthType(TYPE_ARP)
174 + .build();
175 + }
176 +
177 + private TrafficSelector buildNeighborSolicitationSelector() {
178 + return DefaultTrafficSelector.builder()
179 + .matchEthType(TYPE_IPV6)
180 + .matchIPProtocol(PROTOCOL_ICMP6)
181 + .matchIcmpv6Type(NEIGHBOR_SOLICITATION)
182 + .build();
183 + }
184 +
185 + private TrafficSelector buildNeighborAdvertisementSelector() {
186 + return DefaultTrafficSelector.builder()
187 + .matchEthType(TYPE_IPV6)
188 + .matchIPProtocol(PROTOCOL_ICMP6)
189 + .matchIcmpv6Type(NEIGHBOR_ADVERTISEMENT)
190 + .build();
191 + }
192 +
193 + @Override
194 + public void registerNeighbourHandler(ConnectPoint connectPoint, NeighbourMessageHandler handler) {
195 + packetHandlers.put(connectPoint, new HandlerRegistration(handler));
196 + }
197 +
198 + @Override
199 + public void registerNeighbourHandler(Interface intf, NeighbourMessageHandler handler) {
200 + packetHandlers.put(intf.connectPoint(), new HandlerRegistration(handler, intf));
201 + }
202 +
203 + @Override
204 + public void unregisterNeighbourHandler(ConnectPoint connectPoint, NeighbourMessageHandler handler) {
205 + packetHandlers.remove(connectPoint, handler);
206 + }
207 +
208 + @Override
209 + public void unregisterNeighbourHandler(Interface intf, NeighbourMessageHandler handler) {
210 + packetHandlers.remove(intf.connectPoint(), handler);
211 + }
212 +
213 + public void handlePacket(PacketContext context) {
214 + InboundPacket pkt = context.inPacket();
215 + Ethernet ethPkt = pkt.parsed();
216 +
217 + NeighbourMessageContext msgContext =
218 + DefaultNeighbourMessageContext.createContext(ethPkt, pkt.receivedFrom(), actions);
219 +
220 + if (msgContext == null) {
221 + return;
222 + }
223 +
224 + handleMessage(msgContext);
225 +
226 + context.block();
227 + }
228 +
229 + private void handleMessage(NeighbourMessageContext context) {
230 + List<HandlerRegistration> handlers = packetHandlers.get(context.inPort());
231 +
232 + handlers.forEach(registration -> {
233 + if (registration.intf() == null || matches(context, registration.intf())) {
234 + registration.handler().handleMessage(context, hostService);
235 + }
236 + });
237 + }
238 +
239 + private boolean matches(NeighbourMessageContext context, Interface intf) {
240 + checkNotNull(context);
241 + checkNotNull(intf);
242 +
243 + boolean matches = true;
244 + if (!intf.vlan().equals(VlanId.NONE) && !intf.vlan().equals(context.vlan())) {
245 + matches = false;
246 + }
247 +
248 + return matches;
249 + }
250 +
251 +
252 + private void reply(NeighbourMessageContext context, MacAddress targetMac) {
253 + switch (context.protocol()) {
254 + case ARP:
255 + sendTo(ARP.buildArpReply((Ip4Address) context.target(),
256 + targetMac, context.packet()), context.inPort());
257 + break;
258 + case NDP:
259 + sendTo(buildNdpReply((Ip6Address) context.target(), targetMac,
260 + context.packet()), context.inPort());
261 + break;
262 + default:
263 + break;
264 + }
265 + }
266 +
267 + /**
268 + * Outputs a packet out a specific port.
269 + *
270 + * @param packet the packet to send
271 + * @param outPort the port to send it out
272 + */
273 + private void sendTo(Ethernet packet, ConnectPoint outPort) {
274 + sendTo(ByteBuffer.wrap(packet.serialize()), outPort);
275 + }
276 +
277 + /**
278 + * Outputs a packet out a specific port.
279 + *
280 + * @param packet packet to send
281 + * @param outPort port to send it out
282 + */
283 + private void sendTo(ByteBuffer packet, ConnectPoint outPort) {
284 + if (!edgeService.isEdgePoint(outPort)) {
285 + // Sanity check to make sure we don't send the packet out an
286 + // internal port and create a loop (could happen due to
287 + // misconfiguration).
288 + return;
289 + }
290 +
291 + TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
292 + builder.setOutput(outPort.port());
293 + packetService.emit(new DefaultOutboundPacket(outPort.deviceId(),
294 + builder.build(), packet));
295 + }
296 +
297 + /**
298 + * Builds an Neighbor Discovery reply based on a request.
299 + *
300 + * @param srcIp the IP address to use as the reply source
301 + * @param srcMac the MAC address to use as the reply source
302 + * @param request the Neighbor Solicitation request we got
303 + * @return an Ethernet frame containing the Neighbor Advertisement reply
304 + */
305 + private Ethernet buildNdpReply(Ip6Address srcIp, MacAddress srcMac,
306 + Ethernet request) {
307 + Ethernet eth = new Ethernet();
308 + eth.setDestinationMACAddress(request.getSourceMAC());
309 + eth.setSourceMACAddress(srcMac);
310 + eth.setEtherType(Ethernet.TYPE_IPV6);
311 + eth.setVlanID(request.getVlanID());
312 +
313 + IPv6 requestIp = (IPv6) request.getPayload();
314 + IPv6 ipv6 = new IPv6();
315 + ipv6.setSourceAddress(srcIp.toOctets());
316 + ipv6.setDestinationAddress(requestIp.getSourceAddress());
317 + ipv6.setHopLimit((byte) 255);
318 +
319 + ICMP6 icmp6 = new ICMP6();
320 + icmp6.setIcmpType(ICMP6.NEIGHBOR_ADVERTISEMENT);
321 + icmp6.setIcmpCode((byte) 0);
322 +
323 + NeighborAdvertisement nadv = new NeighborAdvertisement();
324 + nadv.setTargetAddress(srcIp.toOctets());
325 + nadv.setSolicitedFlag((byte) 1);
326 + nadv.setOverrideFlag((byte) 1);
327 + nadv.addOption(NeighborDiscoveryOptions.TYPE_TARGET_LL_ADDRESS,
328 + srcMac.toBytes());
329 +
330 + icmp6.setPayload(nadv);
331 + ipv6.setPayload(icmp6);
332 + eth.setPayload(ipv6);
333 + return eth;
334 + }
335 +
336 + /**
337 + * Stores a neighbour message handler registration.
338 + */
339 + private class HandlerRegistration {
340 + private final Interface intf;
341 + private final NeighbourMessageHandler handler;
342 +
343 + /**
344 + * Creates a new handler registration.
345 + *
346 + * @param handler neighbour message handler
347 + */
348 + public HandlerRegistration(NeighbourMessageHandler handler) {
349 + this(handler, null);
350 + }
351 +
352 + /**
353 + * Creates a new handler registration.
354 + *
355 + * @param handler neighbour message handler
356 + * @param intf interface
357 + */
358 + public HandlerRegistration(NeighbourMessageHandler handler, Interface intf) {
359 + this.intf = intf;
360 + this.handler = handler;
361 + }
362 +
363 + /**
364 + * Gets the interface of the registration.
365 + *
366 + * @return interface
367 + */
368 + public Interface intf() {
369 + return intf;
370 + }
371 +
372 + /**
373 + * Gets the neighbour message handler.
374 + *
375 + * @return message handler
376 + */
377 + public NeighbourMessageHandler handler() {
378 + return handler;
379 + }
380 +
381 + @Override
382 + public boolean equals(Object other) {
383 + if (this == other) {
384 + return true;
385 + }
386 +
387 + if (!(other instanceof HandlerRegistration)) {
388 + return false;
389 + }
390 +
391 + HandlerRegistration that = (HandlerRegistration) other;
392 +
393 + return Objects.equals(intf, that.intf) &&
394 + Objects.equals(handler, that.handler);
395 + }
396 +
397 + @Override
398 + public int hashCode() {
399 + return Objects.hash(intf, handler);
400 + }
401 + }
402 +
403 + /**
404 + * Packet processor for incoming packets.
405 + */
406 + private class InternalPacketProcessor implements PacketProcessor {
407 +
408 + @Override
409 + public void process(PacketContext context) {
410 + // Stop processing if the packet has been handled, since we
411 + // can't do any more to it.
412 + if (context.isHandled()) {
413 + return;
414 + }
415 +
416 + InboundPacket pkt = context.inPacket();
417 + Ethernet ethPkt = pkt.parsed();
418 + if (ethPkt == null) {
419 + return;
420 + }
421 +
422 + if (ethPkt.getEtherType() == TYPE_ARP) {
423 + // handle ARP packets
424 + handlePacket(context);
425 + } else if (ethPkt.getEtherType() == TYPE_IPV6) {
426 + IPv6 ipv6 = (IPv6) ethPkt.getPayload();
427 + if (ipv6.getNextHeader() == IPv6.PROTOCOL_ICMP6) {
428 + ICMP6 icmp6 = (ICMP6) ipv6.getPayload();
429 + if (icmp6.getIcmpType() == NEIGHBOR_SOLICITATION ||
430 + icmp6.getIcmpType() == NEIGHBOR_ADVERTISEMENT) {
431 + // handle ICMPv6 solicitations and advertisements (NDP)
432 + handlePacket(context);
433 + }
434 + }
435 + }
436 + }
437 + }
438 +
439 + private class InternalNeighbourMessageActions implements NeighbourMessageActions {
440 +
441 + @Override
442 + public void reply(NeighbourMessageContext context, MacAddress targetMac) {
443 + NeighbourPacketManager.this.reply(context, targetMac);
444 + }
445 +
446 + @Override
447 + public void proxy(NeighbourMessageContext context, ConnectPoint outPort) {
448 + sendTo(context.packet(), outPort);
449 + }
450 +
451 + @Override
452 + public void proxy(NeighbourMessageContext context, Interface outIntf) {
453 +
454 + }
455 +
456 + @Override
457 + public void flood(NeighbourMessageContext context) {
458 + edgeService.getEdgePoints().forEach(connectPoint -> {
459 + if (!connectPoint.equals(context.inPort())) {
460 + sendTo(context.packet(), connectPoint);
461 + }
462 + });
463 + }
464 +
465 + @Override
466 + public void drop(NeighbourMessageContext context) {
467 +
468 + }
469 + }
470 +
471 +}
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +/**
18 + * Implementation of neighbour resolution service.
19 + */
20 +package org.onosproject.incubator.net.neighbour.impl;