blob: a3c8e4b67fa172cd74247235a79c768e6d720677 [file] [log] [blame]
Amit Ghosh47243cb2017-07-26 05:08:53 +01001/*
Deepa vaddireddy0060f532017-08-04 06:46:05 +00002 * Copyright 2017-present Open Networking Foundation
Amit Ghosh47243cb2017-07-26 05:08:53 +01003 *
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 */
16package org.opencord.dhcpl2relay;
17
18import com.google.common.collect.ImmutableSet;
Deepa vaddireddy0060f532017-08-04 06:46:05 +000019import com.google.common.collect.Lists;
Amit Ghosh8951f042017-08-10 13:48:10 +010020import com.google.common.collect.Sets;
Deepa vaddireddy0060f532017-08-04 06:46:05 +000021
Amit Ghosh47243cb2017-07-26 05:08:53 +010022import org.apache.felix.scr.annotations.Activate;
23import org.apache.felix.scr.annotations.Component;
24import org.apache.felix.scr.annotations.Deactivate;
25import org.apache.felix.scr.annotations.Modified;
26import org.apache.felix.scr.annotations.Property;
27import org.apache.felix.scr.annotations.Reference;
28import org.apache.felix.scr.annotations.ReferenceCardinality;
Deepa vaddireddy0060f532017-08-04 06:46:05 +000029import org.onlab.packet.DeserializationException;
Amit Ghosh47243cb2017-07-26 05:08:53 +010030import org.onlab.packet.DHCP;
31import org.onlab.packet.DHCPOption;
32import org.onlab.packet.DHCPPacketType;
Deepa vaddireddy0060f532017-08-04 06:46:05 +000033import org.onlab.packet.Ethernet;
Amit Ghosh47243cb2017-07-26 05:08:53 +010034import org.onlab.packet.IPv4;
Deepa vaddireddy0060f532017-08-04 06:46:05 +000035import org.onlab.packet.MacAddress;
Amit Ghosh47243cb2017-07-26 05:08:53 +010036import org.onlab.packet.TpPort;
37import org.onlab.packet.UDP;
38import org.onlab.packet.VlanId;
Amit Ghosh8951f042017-08-10 13:48:10 +010039import org.onosproject.mastership.MastershipEvent;
40import org.onosproject.mastership.MastershipListener;
41import org.onosproject.mastership.MastershipService;
42import org.onosproject.net.device.DeviceEvent;
43import org.onosproject.net.device.DeviceListener;
Deepa vaddireddy0060f532017-08-04 06:46:05 +000044import org.opencord.dhcpl2relay.packet.DhcpEthernet;
45import org.opencord.dhcpl2relay.packet.DhcpOption82;
Amit Ghosh47243cb2017-07-26 05:08:53 +010046import org.onlab.util.Tools;
47import org.onosproject.cfg.ComponentConfigService;
48import org.onosproject.core.ApplicationId;
49import org.onosproject.core.CoreService;
50import org.onosproject.net.AnnotationKeys;
51import org.onosproject.net.ConnectPoint;
52import org.onosproject.net.Host;
53import org.onosproject.net.Port;
54import org.onosproject.net.config.ConfigFactory;
55import org.onosproject.net.config.NetworkConfigEvent;
56import org.onosproject.net.config.NetworkConfigListener;
57import org.onosproject.net.config.NetworkConfigRegistry;
58import org.onosproject.net.device.DeviceService;
59import org.onosproject.net.flow.DefaultTrafficSelector;
60import org.onosproject.net.flow.DefaultTrafficTreatment;
61import org.onosproject.net.flow.TrafficSelector;
62import org.onosproject.net.flow.TrafficTreatment;
63import org.onosproject.net.host.HostService;
64import org.onosproject.net.packet.DefaultOutboundPacket;
65import org.onosproject.net.packet.OutboundPacket;
66import org.onosproject.net.packet.PacketContext;
67import org.onosproject.net.packet.PacketPriority;
68import org.onosproject.net.packet.PacketProcessor;
69import org.onosproject.net.packet.PacketService;
70
71import org.opencord.sadis.SubscriberAndDeviceInformation;
72import org.opencord.sadis.SubscriberAndDeviceInformationService;
Amit Ghosh47243cb2017-07-26 05:08:53 +010073import org.osgi.service.component.ComponentContext;
74import org.slf4j.Logger;
75import org.slf4j.LoggerFactory;
76
77import java.nio.ByteBuffer;
78import java.util.Dictionary;
Deepa vaddireddy0060f532017-08-04 06:46:05 +000079import java.util.List;
Deepa vaddireddy0060f532017-08-04 06:46:05 +000080import java.util.Set;
Amit Ghosh8951f042017-08-10 13:48:10 +010081import java.util.concurrent.atomic.AtomicReference;
Deepa vaddireddy0060f532017-08-04 06:46:05 +000082import java.util.stream.Collectors;
Amit Ghosh47243cb2017-07-26 05:08:53 +010083
84import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_MessageType;
85import static org.onlab.packet.MacAddress.valueOf;
86import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
87
88/**
89 * DHCP Relay Agent Application Component.
90 */
91@Component(immediate = true)
92public class DhcpL2Relay {
93
94 public static final String DHCP_L2RELAY_APP = "org.opencord.dhcpl2relay";
95 private final Logger log = LoggerFactory.getLogger(getClass());
96 private final InternalConfigListener cfgListener =
97 new InternalConfigListener();
98
99 private final Set<ConfigFactory> factories = ImmutableSet.of(
100 new ConfigFactory<ApplicationId, DhcpL2RelayConfig>(APP_SUBJECT_FACTORY,
101 DhcpL2RelayConfig.class,
102 "dhcpl2relay") {
103 @Override
104 public DhcpL2RelayConfig createConfig() {
105 return new DhcpL2RelayConfig();
106 }
107 }
108 );
109
110 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
111 protected NetworkConfigRegistry cfgService;
112
113 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
114 protected CoreService coreService;
115
116 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
117 protected PacketService packetService;
118
119 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
120 protected HostService hostService;
121
122 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
123 protected ComponentConfigService componentConfigService;
124
125 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
126 protected SubscriberAndDeviceInformationService subsService;
127
128 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
129 protected DeviceService deviceService;
130
Amit Ghosh8951f042017-08-10 13:48:10 +0100131 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
132 protected MastershipService mastershipService;
133
Amit Ghosh47243cb2017-07-26 05:08:53 +0100134 @Property(name = "option82", boolValue = true,
135 label = "Add option 82 to relayed packets")
136 protected boolean option82 = true;
137
138 private DhcpRelayPacketProcessor dhcpRelayPacketProcessor =
139 new DhcpRelayPacketProcessor();
140
Amit Ghosh8951f042017-08-10 13:48:10 +0100141 private InnerMastershipListener changeListener = new InnerMastershipListener();
142 private InnerDeviceListener deviceListener = new InnerDeviceListener();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100143
Amit Ghosh8951f042017-08-10 13:48:10 +0100144 // connect points to the DHCP server
145 Set<ConnectPoint> dhcpConnectPoints;
146 private AtomicReference<ConnectPoint> dhcpServerConnectPoint = new AtomicReference<>();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100147 private MacAddress dhcpConnectMac = MacAddress.BROADCAST;
148 private ApplicationId appId;
149
150 @Activate
151 protected void activate(ComponentContext context) {
152 //start the dhcp relay agent
153 appId = coreService.registerApplication(DHCP_L2RELAY_APP);
154 componentConfigService.registerProperties(getClass());
155
156 cfgService.addListener(cfgListener);
Amit Ghosh8951f042017-08-10 13:48:10 +0100157 mastershipService.addListener(changeListener);
158 deviceService.addListener(deviceListener);
159
Amit Ghosh47243cb2017-07-26 05:08:53 +0100160 factories.forEach(cfgService::registerConfigFactory);
161 //update the dhcp server configuration.
162 updateConfig();
163 //add the packet services.
164 packetService.addProcessor(dhcpRelayPacketProcessor,
165 PacketProcessor.director(0));
166 requestDhcpPackets();
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000167 if (context != null) {
168 modified(context);
169 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100170
171 log.info("DHCP-L2-RELAY Started");
172 }
173
174 @Deactivate
175 protected void deactivate() {
176 cfgService.removeListener(cfgListener);
177 factories.forEach(cfgService::unregisterConfigFactory);
178 packetService.removeProcessor(dhcpRelayPacketProcessor);
179 cancelDhcpPackets();
180
181 componentConfigService.unregisterProperties(getClass(), false);
182
183 log.info("DHCP-L2-RELAY Stopped");
184 }
185
186 @Modified
187 protected void modified(ComponentContext context) {
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000188
Amit Ghosh47243cb2017-07-26 05:08:53 +0100189 Dictionary<?, ?> properties = context.getProperties();
190
191 Boolean o = Tools.isPropertyEnabled(properties, "option82");
192 if (o != null) {
193 option82 = o;
194 }
195 }
196
197 /**
198 * Checks if this app has been configured.
199 *
200 * @return true if all information we need have been initialized
201 */
202 private boolean configured() {
Amit Ghosh8951f042017-08-10 13:48:10 +0100203 return dhcpServerConnectPoint.get() != null;
Amit Ghosh47243cb2017-07-26 05:08:53 +0100204 }
205
Amit Ghosh8951f042017-08-10 13:48:10 +0100206 /**
207 * Selects a connect point through an available device for which it is the master.
208 */
209 private void selectServerConnectPoint() {
210 synchronized (this) {
211 dhcpServerConnectPoint.set(null);
212 if (dhcpConnectPoints != null) {
213 // find a connect point through a device for which we are master
214 for (ConnectPoint cp: dhcpConnectPoints) {
215 if (mastershipService.isLocalMaster(cp.deviceId())) {
216 if (deviceService.isAvailable(cp.deviceId())) {
217 dhcpServerConnectPoint.set(cp);
218 }
219 log.info("DHCP connectPoint selected is {}", cp);
220 break;
221 }
222 }
223 }
224
225 log.info("DHCP Server connectPoint is {}", dhcpServerConnectPoint.get());
226
227 if (dhcpServerConnectPoint.get() == null) {
228 log.error("Master of none, can't relay DHCP Message to server");
229 }
230 }
231 }
232
233 /**
234 * Updates the network configuration.
235 */
Amit Ghosh47243cb2017-07-26 05:08:53 +0100236 private void updateConfig() {
237 DhcpL2RelayConfig cfg = cfgService.getConfig(appId, DhcpL2RelayConfig.class);
238 if (cfg == null) {
239 log.warn("Dhcp Server info not available");
240 return;
241 }
Amit Ghosh8951f042017-08-10 13:48:10 +0100242
243 dhcpConnectPoints = Sets.newConcurrentHashSet(cfg.getDhcpServerConnectPoint());
244
245 selectServerConnectPoint();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100246
Amit Ghosh47243cb2017-07-26 05:08:53 +0100247 log.info("dhcp server connect point: " + dhcpServerConnectPoint);
248 }
249
250 /**
251 * Request DHCP packet in via PacketService.
252 */
253 private void requestDhcpPackets() {
Amit Ghosh8951f042017-08-10 13:48:10 +0100254 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
255 .matchEthType(Ethernet.TYPE_IPV4)
256 .matchIPProtocol(IPv4.PROTOCOL_UDP)
257 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT));
258 packetService.requestPackets(selectorServer.build(),
259 PacketPriority.CONTROL, appId);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100260
Amit Ghosh8951f042017-08-10 13:48:10 +0100261 TrafficSelector.Builder selectorClient = DefaultTrafficSelector.builder()
262 .matchEthType(Ethernet.TYPE_IPV4)
263 .matchIPProtocol(IPv4.PROTOCOL_UDP)
264 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
265 packetService.requestPackets(selectorClient.build(),
266 PacketPriority.CONTROL, appId);
267
Amit Ghosh47243cb2017-07-26 05:08:53 +0100268 }
269
270 /**
271 * Cancel requested DHCP packets in via packet service.
272 */
273 private void cancelDhcpPackets() {
Amit Ghosh8951f042017-08-10 13:48:10 +0100274 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
275 .matchEthType(Ethernet.TYPE_IPV4)
276 .matchIPProtocol(IPv4.PROTOCOL_UDP)
277 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT));
278 packetService.cancelPackets(selectorServer.build(),
279 PacketPriority.CONTROL, appId);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100280
Amit Ghosh8951f042017-08-10 13:48:10 +0100281 TrafficSelector.Builder selectorClient = DefaultTrafficSelector.builder()
282 .matchEthType(Ethernet.TYPE_IPV4)
283 .matchIPProtocol(IPv4.PROTOCOL_UDP)
284 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
285 packetService.cancelPackets(selectorClient.build(),
286 PacketPriority.CONTROL, appId);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100287 }
288
289 private SubscriberAndDeviceInformation getDevice(PacketContext context) {
290 String serialNo = deviceService.getDevice(context.inPacket().
291 receivedFrom().deviceId()).serialNumber();
292
293 return subsService.get(serialNo);
294 }
295
296 private SubscriberAndDeviceInformation getDevice(ConnectPoint cp) {
297 String serialNo = deviceService.getDevice(cp.deviceId()).
298 serialNumber();
299
300 return subsService.get(serialNo);
301 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100302
303 private MacAddress relayAgentMacAddress(PacketContext context) {
304
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000305 SubscriberAndDeviceInformation device = this.getDevice(context);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100306 if (device == null) {
307 log.warn("Device not found for {}", context.inPacket().
308 receivedFrom());
309 return null;
310 }
311
312 return device.hardwareIdentifier();
313 }
314
315 private String nasPortId(PacketContext context) {
Amit Ghosh8951f042017-08-10 13:48:10 +0100316 return nasPortId(context.inPacket().receivedFrom());
317 }
318
319 private String nasPortId(ConnectPoint cp) {
320 Port p = deviceService.getPort(cp);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100321 return p.annotations().value(AnnotationKeys.PORT_NAME);
322 }
323
324 private SubscriberAndDeviceInformation getSubscriber(PacketContext context) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100325 return subsService.get(nasPortId(context));
326 }
327
328 private VlanId cTag(PacketContext context) {
329 SubscriberAndDeviceInformation sub = getSubscriber(context);
330 if (sub == null) {
331 log.warn("Subscriber info not found for {}", context.inPacket().
332 receivedFrom());
333 return VlanId.NONE;
334 }
335 return sub.cTag();
336 }
337
Amit Ghosh8951f042017-08-10 13:48:10 +0100338 private VlanId cTag(ConnectPoint cp) {
339 String portId = nasPortId(cp);
340 SubscriberAndDeviceInformation sub = subsService.get(portId);
341 if (sub == null) {
342 log.warn("Subscriber info not found for {} looking for C-TAG", cp);
343 return VlanId.NONE;
344 }
345 return sub.cTag();
346 }
347
348 private VlanId sTag(ConnectPoint cp) {
349 String portId = nasPortId(cp);
350 SubscriberAndDeviceInformation sub = subsService.get(portId);
351 if (sub == null) {
352 log.warn("Subscriber info not found for {} looking for S-TAG", cp);
353 return VlanId.NONE;
354 }
355 return sub.sTag();
356 }
357
Amit Ghosh47243cb2017-07-26 05:08:53 +0100358 private VlanId sTag(PacketContext context) {
359 SubscriberAndDeviceInformation sub = getSubscriber(context);
360 if (sub == null) {
361 log.warn("Subscriber info not found for {}", context.inPacket().
362 receivedFrom());
363 return VlanId.NONE;
364 }
365 return sub.sTag();
366 }
367
368 private class DhcpRelayPacketProcessor implements PacketProcessor {
369
370 @Override
371 public void process(PacketContext context) {
372 if (!configured()) {
373 log.warn("Missing DHCP relay config. Abort packet processing");
374 return;
375 }
376
377 // process the packet and get the payload
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000378 DhcpEthernet packet = null;
379 ByteBuffer byteBuffer = context.inPacket().unparsed();
380 try {
381 packet = DhcpEthernet.deserializer().deserialize(byteBuffer.array(), 0, byteBuffer.array().length);
382 } catch (DeserializationException e) {
383 log.warn("Unable to deserialize packet");
384 }
385
Amit Ghosh47243cb2017-07-26 05:08:53 +0100386 if (packet == null) {
387 log.warn("Packet is null");
388 return;
389 }
390
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000391 log.debug("Got a packet ", packet);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100392
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000393 if (packet.getEtherType() == DhcpEthernet.TYPE_IPV4) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100394 IPv4 ipv4Packet = (IPv4) packet.getPayload();
395
396 if (ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
397 UDP udpPacket = (UDP) ipv4Packet.getPayload();
398 if (udpPacket.getSourcePort() == UDP.DHCP_CLIENT_PORT ||
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000399 udpPacket.getSourcePort() == UDP.DHCP_SERVER_PORT) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100400 DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
401 //This packet is dhcp.
402 processDhcpPacket(context, packet, dhcpPayload);
403 }
404 }
405 }
406 }
407
408 //forward the packet to ConnectPoint where the DHCP server is attached.
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000409 private void forwardPacket(DhcpEthernet packet) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100410
Amit Ghosh8951f042017-08-10 13:48:10 +0100411 if (dhcpServerConnectPoint.get() != null) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100412 TrafficTreatment t = DefaultTrafficTreatment.builder()
Amit Ghosh8951f042017-08-10 13:48:10 +0100413 .setOutput(dhcpServerConnectPoint.get().port()).build();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100414 OutboundPacket o = new DefaultOutboundPacket(
Amit Ghosh8951f042017-08-10 13:48:10 +0100415 dhcpServerConnectPoint.get().deviceId(), t,
Amit Ghosh47243cb2017-07-26 05:08:53 +0100416 ByteBuffer.wrap(packet.serialize()));
417 if (log.isTraceEnabled()) {
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000418 log.trace("Relaying packet to dhcp server {} at {}",
Amit Ghosh8951f042017-08-10 13:48:10 +0100419 packet, dhcpServerConnectPoint.get());
Amit Ghosh47243cb2017-07-26 05:08:53 +0100420 }
421 packetService.emit(o);
422 } else {
423 log.warn("No dhcp server connect point");
424 }
425 }
426
427 //process the dhcp packet before sending to server
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000428 private void processDhcpPacket(PacketContext context, DhcpEthernet packet,
Amit Ghosh47243cb2017-07-26 05:08:53 +0100429 DHCP dhcpPayload) {
430 if (dhcpPayload == null) {
431 log.warn("DHCP payload is null");
432 return;
433 }
434
435 DHCPPacketType incomingPacketType = null;
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000436
Amit Ghosh47243cb2017-07-26 05:08:53 +0100437 for (DHCPOption option : dhcpPayload.getOptions()) {
438 if (option.getCode() == OptionCode_MessageType.getValue()) {
439 byte[] data = option.getData();
440 incomingPacketType = DHCPPacketType.getType(data[0]);
441 }
442 }
443 log.info("Received DHCP Packet of type {}", incomingPacketType);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100444
445 switch (incomingPacketType) {
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000446 case DHCPDISCOVER:
447 DhcpEthernet ethernetPacketDiscover =
448 processDhcpPacketFromClient(context, packet);
449 if (ethernetPacketDiscover != null) {
450 forwardPacket(ethernetPacketDiscover);
451 }
452 break;
453 case DHCPOFFER:
454 //reply to dhcp client.
455 DhcpEthernet ethernetPacketOffer = processDhcpPacketFromServer(packet);
456 if (ethernetPacketOffer != null) {
457 sendReply(ethernetPacketOffer, dhcpPayload);
458 }
459 break;
460 case DHCPREQUEST:
461 DhcpEthernet ethernetPacketRequest =
462 processDhcpPacketFromClient(context, packet);
463 if (ethernetPacketRequest != null) {
464 forwardPacket(ethernetPacketRequest);
465 }
466 break;
467 case DHCPACK:
468 //reply to dhcp client.
469 DhcpEthernet ethernetPacketAck = processDhcpPacketFromServer(packet);
470 if (ethernetPacketAck != null) {
471 sendReply(ethernetPacketAck, dhcpPayload);
472 }
473 break;
474 default:
475 break;
Amit Ghosh47243cb2017-07-26 05:08:53 +0100476 }
477 }
478
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000479 private DhcpEthernet processDhcpPacketFromClient(PacketContext context,
480 DhcpEthernet ethernetPacket) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100481 log.info("Processing packet from client");
482
483 MacAddress relayAgentMac = relayAgentMacAddress(context);
484 if (relayAgentMac == null) {
485 log.warn("RelayAgent MAC not found ");
Amit Ghosh47243cb2017-07-26 05:08:53 +0100486 return null;
487 }
488
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000489 DhcpEthernet etherReply = ethernetPacket;
Amit Ghosh47243cb2017-07-26 05:08:53 +0100490
491 IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
492 UDP udpPacket = (UDP) ipv4Packet.getPayload();
493 DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
494
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000495 if (option82) {
496 SubscriberAndDeviceInformation entry = getSubscriber(context);
497 if (entry == null) {
498 log.info("Dropping packet as subscriber entry is not available");
499 return null;
500 }
501
502 DHCP dhcpPacketWithOption82 = addOption82(dhcpPacket, entry);
503 udpPacket.setPayload(dhcpPacketWithOption82);
504 }
505
506 ipv4Packet.setPayload(udpPacket);
507 etherReply.setPayload(ipv4Packet);
508 etherReply.setSourceMacAddress(relayAgentMac);
509 etherReply.setDestinationMacAddress(dhcpConnectMac);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100510
Amit Ghosh8951f042017-08-10 13:48:10 +0100511 etherReply.setPriorityCode(ethernetPacket.getPriorityCode());
Amit Ghosh47243cb2017-07-26 05:08:53 +0100512 etherReply.setVlanID(cTag(context).toShort());
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000513 etherReply.setQinQtpid(DhcpEthernet.TYPE_QINQ);
514 etherReply.setQinQVid(sTag(context).toShort());
Amit Ghosh47243cb2017-07-26 05:08:53 +0100515
Amit Ghosh8951f042017-08-10 13:48:10 +0100516 log.info("Finished processing packet -- sending packet {}", etherReply);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100517 return etherReply;
518 }
519
520 //build the DHCP offer/ack with proper client port.
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000521 private DhcpEthernet processDhcpPacketFromServer(DhcpEthernet ethernetPacket) {
522 log.info("Processing DHCP packet from server");
Amit Ghosh47243cb2017-07-26 05:08:53 +0100523 // get dhcp header.
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000524 DhcpEthernet etherReply = (DhcpEthernet) ethernetPacket.clone();
Amit Ghosh47243cb2017-07-26 05:08:53 +0100525 IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
526 UDP udpPacket = (UDP) ipv4Packet.getPayload();
527 DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
528
Amit Ghosh47243cb2017-07-26 05:08:53 +0100529 MacAddress dstMac = valueOf(dhcpPayload.getClientHardwareAddress());
530 Set<Host> hosts = hostService.getHostsByMac(dstMac);
531 if (hosts == null || hosts.isEmpty()) {
532 log.warn("Cannot determine host for DHCP client: {}. Aborting "
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000533 + "relay for dhcp packet from server {}",
534 dstMac, ethernetPacket);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100535 return null;
536 } else if (hosts.size() > 1) {
537 // XXX redo to send reply to all hosts found
538 log.warn("Multiple hosts found for mac:{}. Picking one "
539 + "host out of {}", dstMac, hosts);
540 }
541 Host host = hosts.iterator().next();
542
Amit Ghosh8951f042017-08-10 13:48:10 +0100543 ConnectPoint subsCp = new ConnectPoint(host.location().deviceId(),
544 host.location().port());
Amit Ghosh47243cb2017-07-26 05:08:53 +0100545
546 // we leave the srcMac from the original packet
Amit Ghosh8951f042017-08-10 13:48:10 +0100547 etherReply.setDestinationMacAddress(dstMac);
548 etherReply.setQinQVid(sTag(subsCp).toShort());
549 etherReply.setPriorityCode(ethernetPacket.getPriorityCode());
550 etherReply.setVlanID((cTag(subsCp).toShort()));
Amit Ghosh47243cb2017-07-26 05:08:53 +0100551
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000552 if (option82) {
553 udpPacket.setPayload(removeOption82(dhcpPayload));
554 } else {
555 udpPacket.setPayload(dhcpPayload);
556 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100557 ipv4Packet.setPayload(udpPacket);
558 etherReply.setPayload(ipv4Packet);
559
560 log.info("Finished processing packet");
561 return etherReply;
562 }
563
564 //send the response to the requester host.
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000565 private void sendReply(DhcpEthernet ethPacket, DHCP dhcpPayload) {
Amit Ghosh47243cb2017-07-26 05:08:53 +0100566 MacAddress descMac = valueOf(dhcpPayload.getClientHardwareAddress());
567 Host host = hostService.getHostsByMac(descMac).stream().findFirst().orElse(null);
568
569 // Send packet out to requester if the host information is available
570 if (host != null) {
571 log.info("Sending DHCP packet to host: {}", host);
572 TrafficTreatment t = DefaultTrafficTreatment.builder()
573 .setOutput(host.location().port()).build();
574 OutboundPacket o = new DefaultOutboundPacket(
575 host.location().deviceId(), t, ByteBuffer.wrap(ethPacket.serialize()));
576 if (log.isTraceEnabled()) {
577 log.trace("Relaying packet to dhcp client {}", ethPacket);
578 }
579 packetService.emit(o);
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000580 log.info("DHCP Packet sent to {}", host.location());
Amit Ghosh47243cb2017-07-26 05:08:53 +0100581 } else {
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000582 log.error("Dropping DHCP packet because can't find host for {}", descMac);
Amit Ghosh47243cb2017-07-26 05:08:53 +0100583 }
584 }
585 }
586
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000587 private DHCP addOption82(DHCP dhcpPacket, SubscriberAndDeviceInformation entry) {
588 log.debug("option82data {} ", entry);
589
590 List<DHCPOption> options = Lists.newArrayList(dhcpPacket.getOptions());
591 DhcpOption82 option82 = new DhcpOption82();
592 option82.setAgentCircuitId(entry.circuitId());
593 option82.setAgentRemoteId(entry.remoteId());
594 DHCPOption option = new DHCPOption()
595 .setCode(DHCP.DHCPOptionCode.OptionCode_CircuitID.getValue())
596 .setData(option82.toByteArray())
597 .setLength(option82.length());
598
599 options.add(options.size() - 1, option);
600 dhcpPacket.setOptions(options);
Amit Ghosh8951f042017-08-10 13:48:10 +0100601
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000602 return dhcpPacket;
603
604 }
605
606 private DHCP removeOption82(DHCP dhcpPacket) {
607 List<DHCPOption> options = dhcpPacket.getOptions();
608 List<DHCPOption> newoptions = options.stream()
609 .filter(option -> option.getCode() != DHCP.DHCPOptionCode.OptionCode_CircuitID.getValue())
610 .collect(Collectors.toList());
611
612 return dhcpPacket.setOptions(newoptions);
613 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100614 /**
615 * Listener for network config events.
616 */
617 private class InternalConfigListener implements NetworkConfigListener {
618
619 @Override
620 public void event(NetworkConfigEvent event) {
621
622 if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
623 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) &&
624 event.configClass().equals(DhcpL2RelayConfig.class)) {
625 updateConfig();
626 log.info("Reconfigured");
627 }
628 }
629 }
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000630
Amit Ghosh8951f042017-08-10 13:48:10 +0100631 /**
632 * Handles Mastership changes for the devices which connect
633 * to the DHCP server.
634 */
635 private class InnerMastershipListener implements MastershipListener {
636 @Override
637 public void event(MastershipEvent event) {
638 if (dhcpServerConnectPoint.get() != null &&
639 dhcpServerConnectPoint.get().deviceId().
640 equals(event.subject())) {
641 log.trace("Mastership Event recevived for {}", event.subject());
642 // mastership of the device for our connect point has changed
643 // reselect
644 selectServerConnectPoint();
645 }
646 }
647 }
Deepa vaddireddy0060f532017-08-04 06:46:05 +0000648
Amit Ghosh8951f042017-08-10 13:48:10 +0100649 /**
650 * Handles Device status change for the devices which connect
651 * to the DHCP server.
652 */
653 private class InnerDeviceListener implements DeviceListener {
654 @Override
655 public void event(DeviceEvent event) {
656 log.trace("Device Event recevived for {} event {}", event.subject(), event.type());
657 if (dhcpServerConnectPoint.get() == null) {
658 switch (event.type()) {
659 case DEVICE_ADDED:
660 case DEVICE_AVAILABILITY_CHANGED:
661 // some device is available check if we can get one
662 selectServerConnectPoint();
663 break;
664 default:
665 break;
666 }
667 return;
668 }
669 if (dhcpServerConnectPoint.get().deviceId().
670 equals(event.subject().id())) {
671 switch (event.type()) {
672 case DEVICE_AVAILABILITY_CHANGED:
673 case DEVICE_REMOVED:
674 case DEVICE_SUSPENDED:
675 // state of our device has changed, check if we need
676 // to re-select
677 selectServerConnectPoint();
678 break;
679 default:
680 break;
681 }
682 }
683 }
684 }
Amit Ghosh47243cb2017-07-26 05:08:53 +0100685}