blob: 778f778182501b307475615a598707882545cfe5 [file] [log] [blame]
gauravf0884562016-06-17 02:47:13 +05301/*
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 */
16package org.onosproject.dhcprelay;
17
gaurav4af95fb2016-07-07 01:48:44 +053018import java.nio.ByteBuffer;
Pier Luigi20557c82017-01-12 22:40:46 -080019import java.util.Dictionary;
gaurav38351de2016-07-27 04:44:33 +053020import java.util.Objects;
gauravf0884562016-06-17 02:47:13 +053021import java.util.Set;
gauravf0884562016-06-17 02:47:13 +053022import org.apache.felix.scr.annotations.Activate;
23import org.apache.felix.scr.annotations.Component;
24import org.apache.felix.scr.annotations.Deactivate;
Pier Luigi20557c82017-01-12 22:40:46 -080025import org.apache.felix.scr.annotations.Modified;
26import org.apache.felix.scr.annotations.Property;
gauravf0884562016-06-17 02:47:13 +053027import org.apache.felix.scr.annotations.Reference;
28import org.apache.felix.scr.annotations.ReferenceCardinality;
gaurav38351de2016-07-27 04:44:33 +053029import org.onlab.packet.ARP;
gauravf0884562016-06-17 02:47:13 +053030import org.onlab.packet.DHCP;
Yi Tsengc7403c22017-06-19 16:23:22 -070031import org.onlab.packet.dhcp.DhcpOption;
gauravf0884562016-06-17 02:47:13 +053032import org.onlab.packet.Ethernet;
33import org.onlab.packet.IPv4;
gaurav38351de2016-07-27 04:44:33 +053034import org.onlab.packet.Ip4Address;
Charles Chan23d15bc2017-05-13 00:55:21 -070035import org.onlab.packet.IpAddress;
gaurav38351de2016-07-27 04:44:33 +053036import org.onlab.packet.MacAddress;
gauravf0884562016-06-17 02:47:13 +053037import org.onlab.packet.TpPort;
38import org.onlab.packet.UDP;
gaurav4af95fb2016-07-07 01:48:44 +053039import org.onlab.packet.VlanId;
Pier Luigi20557c82017-01-12 22:40:46 -080040import org.onlab.util.Tools;
gauravf0884562016-06-17 02:47:13 +053041import org.onosproject.core.ApplicationId;
42import org.onosproject.core.CoreService;
gaurav38351de2016-07-27 04:44:33 +053043import org.onosproject.incubator.net.intf.Interface;
44import org.onosproject.incubator.net.intf.InterfaceService;
gauravf0884562016-06-17 02:47:13 +053045import org.onosproject.net.ConnectPoint;
46import org.onosproject.net.Host;
47import org.onosproject.net.HostId;
48import org.onosproject.net.config.ConfigFactory;
49import org.onosproject.net.config.NetworkConfigEvent;
50import org.onosproject.net.config.NetworkConfigListener;
51import org.onosproject.net.config.NetworkConfigRegistry;
52import org.onosproject.net.flow.DefaultTrafficSelector;
53import org.onosproject.net.flow.DefaultTrafficTreatment;
54import org.onosproject.net.flow.TrafficSelector;
55import org.onosproject.net.flow.TrafficTreatment;
Saurav Dasb0ae6ee2017-03-04 16:08:47 -080056import org.onosproject.net.host.HostEvent;
57import org.onosproject.net.host.HostListener;
gauravf0884562016-06-17 02:47:13 +053058import org.onosproject.net.host.HostService;
Saurav Dasb0ae6ee2017-03-04 16:08:47 -080059import org.onosproject.net.host.InterfaceIpAddress;
gauravf0884562016-06-17 02:47:13 +053060import org.onosproject.net.packet.DefaultOutboundPacket;
61import org.onosproject.net.packet.OutboundPacket;
62import org.onosproject.net.packet.PacketContext;
63import org.onosproject.net.packet.PacketPriority;
64import org.onosproject.net.packet.PacketProcessor;
65import org.onosproject.net.packet.PacketService;
Pier Luigi20557c82017-01-12 22:40:46 -080066import org.osgi.service.component.ComponentContext;
gauravf0884562016-06-17 02:47:13 +053067import org.slf4j.Logger;
68import org.slf4j.LoggerFactory;
69
70import com.google.common.collect.ImmutableSet;
gaurav38351de2016-07-27 04:44:33 +053071
gauravf0884562016-06-17 02:47:13 +053072import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
gaurav4af95fb2016-07-07 01:48:44 +053073import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_MessageType;
gaurav38351de2016-07-27 04:44:33 +053074import static org.onlab.packet.MacAddress.valueOf;
gauravf0884562016-06-17 02:47:13 +053075/**
76 * DHCP Relay Agent Application Component.
77 */
78@Component(immediate = true)
79public class DhcpRelay {
80
81 public static final String DHCP_RELAY_APP = "org.onosproject.dhcp-relay";
82 private final Logger log = LoggerFactory.getLogger(getClass());
83 private final InternalConfigListener cfgListener = new InternalConfigListener();
gaurav38351de2016-07-27 04:44:33 +053084 private static MacAddress myMAC = valueOf("4f:4f:4f:4f:4f:4f");
gauravf0884562016-06-17 02:47:13 +053085
86 private final Set<ConfigFactory> factories = ImmutableSet.of(
87 new ConfigFactory<ApplicationId, DhcpRelayConfig>(APP_SUBJECT_FACTORY,
88 DhcpRelayConfig.class,
89 "dhcprelay") {
90 @Override
91 public DhcpRelayConfig createConfig() {
92 return new DhcpRelayConfig();
93 }
94 }
95 );
96
97 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
98 protected NetworkConfigRegistry cfgService;
99
100 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
101 protected CoreService coreService;
102
103 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
104 protected PacketService packetService;
105
106 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
107 protected HostService hostService;
108
gaurav38351de2016-07-27 04:44:33 +0530109 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
110 protected InterfaceService interfaceService;
111
Pier Luigi20557c82017-01-12 22:40:46 -0800112 @Property(name = "arpEnabled", boolValue = true,
113 label = "Enable Address resolution protocol")
114 protected boolean arpEnabled = true;
115
gauravf0884562016-06-17 02:47:13 +0530116 private DhcpRelayPacketProcessor dhcpRelayPacketProcessor = new DhcpRelayPacketProcessor();
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800117 private InternalHostListener hostListener = new InternalHostListener();
118
gaurav38351de2016-07-27 04:44:33 +0530119 private Ip4Address dhcpServerIp = null;
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800120 // dhcp server may be connected directly to the SDN network or
121 // via an external gateway. When connected directly, the dhcpConnectPoint, dhcpConnectMac,
122 // and dhcpConnectVlan refer to the server. When connected via the gateway, they refer
123 // to the gateway.
124 private ConnectPoint dhcpServerConnectPoint = null;
125 private MacAddress dhcpConnectMac = null;
126 private VlanId dhcpConnectVlan = null;
127 private Ip4Address dhcpGatewayIp = null;
gauravf0884562016-06-17 02:47:13 +0530128 private ApplicationId appId;
129
130 @Activate
Pier Luigi20557c82017-01-12 22:40:46 -0800131 protected void activate(ComponentContext context) {
gauravf0884562016-06-17 02:47:13 +0530132 //start the dhcp relay agent
gauravf0884562016-06-17 02:47:13 +0530133 appId = coreService.registerApplication(DHCP_RELAY_APP);
134
135 cfgService.addListener(cfgListener);
136 factories.forEach(cfgService::registerConfigFactory);
137 //update the dhcp server configuration.
138 updateConfig();
139 //add the packet services.
140 packetService.addProcessor(dhcpRelayPacketProcessor, PacketProcessor.director(0));
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800141 hostService.addListener(hostListener);
Pier Luigi20557c82017-01-12 22:40:46 -0800142 requestDhcpPackets();
143 modified(context);
144
gauravf0884562016-06-17 02:47:13 +0530145 log.info("DHCP-RELAY Started");
gauravf0884562016-06-17 02:47:13 +0530146 }
147
148 @Deactivate
149 protected void deactivate() {
150 cfgService.removeListener(cfgListener);
151 factories.forEach(cfgService::unregisterConfigFactory);
152 packetService.removeProcessor(dhcpRelayPacketProcessor);
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800153 hostService.removeListener(hostListener);
Pier Luigi20557c82017-01-12 22:40:46 -0800154 cancelDhcpPackets();
155 cancelArpPackets();
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800156 if (dhcpGatewayIp != null) {
157 hostService.stopMonitoringIp(dhcpGatewayIp);
158 } else {
159 hostService.stopMonitoringIp(dhcpServerIp);
160 }
Pier Luigi20557c82017-01-12 22:40:46 -0800161
gauravf0884562016-06-17 02:47:13 +0530162 log.info("DHCP-RELAY Stopped");
163 }
164
Pier Luigi20557c82017-01-12 22:40:46 -0800165 @Modified
166 protected void modified(ComponentContext context) {
167 Dictionary<?, ?> properties = context.getProperties();
168 Boolean flag;
169
170 flag = Tools.isPropertyEnabled(properties, "arpEnabled");
171 if (flag != null) {
172 arpEnabled = flag;
173 log.info("Address resolution protocol is {}",
174 arpEnabled ? "enabled" : "disabled");
175 }
176
177 if (arpEnabled) {
178 requestArpPackets();
179 } else {
180 cancelArpPackets();
181 }
182 }
183
Charles Chan3c1691a2017-02-16 14:55:22 -0800184 /**
185 * Checks if this app has been configured.
186 *
187 * @return true if all information we need have been initialized
188 */
189 private boolean configured() {
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800190 return dhcpServerConnectPoint != null && dhcpServerIp != null;
Charles Chan3c1691a2017-02-16 14:55:22 -0800191 }
192
gauravf0884562016-06-17 02:47:13 +0530193 private void updateConfig() {
194 DhcpRelayConfig cfg = cfgService.getConfig(appId, DhcpRelayConfig.class);
gauravf0884562016-06-17 02:47:13 +0530195 if (cfg == null) {
196 log.warn("Dhcp Server info not available");
197 return;
198 }
199
200 dhcpServerConnectPoint = cfg.getDhcpServerConnectPoint();
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800201 Ip4Address oldDhcpServerIp = dhcpServerIp;
202 Ip4Address oldDhcpGatewayIp = dhcpGatewayIp;
gaurav38351de2016-07-27 04:44:33 +0530203 dhcpServerIp = cfg.getDhcpServerIp();
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800204 dhcpGatewayIp = cfg.getDhcpGatewayIp();
205 dhcpConnectMac = null; // reset for updated config
206 dhcpConnectVlan = null; // reset for updated config
Charles Chan23d15bc2017-05-13 00:55:21 -0700207 log.info("DHCP server connect point: " + dhcpServerConnectPoint);
208 log.info("DHCP server ipaddress " + dhcpServerIp);
209
210 IpAddress ipToProbe = dhcpGatewayIp != null ? dhcpGatewayIp : dhcpServerIp;
211 String hostToProbe = dhcpGatewayIp != null ? "gateway" : "DHCP server";
212
213 Set<Host> hosts = hostService.getHostsByIp(ipToProbe);
214 if (hosts.isEmpty()) {
215 log.info("Probing to resolve {} IP {}", hostToProbe, ipToProbe);
216 if (oldDhcpGatewayIp != null) {
217 hostService.stopMonitoringIp(oldDhcpGatewayIp);
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800218 }
Charles Chan23d15bc2017-05-13 00:55:21 -0700219 if (oldDhcpServerIp != null) {
220 hostService.stopMonitoringIp(oldDhcpServerIp);
221 }
222 hostService.startMonitoringIp(ipToProbe);
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800223 } else {
Charles Chan23d15bc2017-05-13 00:55:21 -0700224 // Probe target is known; There should be only 1 host with this ip
225 hostUpdated(hosts.iterator().next());
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800226 }
227 }
228
229 private void hostRemoved(Host host) {
230 if (host.ipAddresses().contains(dhcpServerIp)) {
231 log.warn("DHCP server {} removed", dhcpServerIp);
232 dhcpConnectMac = null;
233 dhcpConnectVlan = null;
234 }
235 if (dhcpGatewayIp != null && host.ipAddresses().contains(dhcpGatewayIp)) {
236 log.warn("DHCP gateway {} removed", dhcpGatewayIp);
237 dhcpConnectMac = null;
238 dhcpConnectVlan = null;
239 }
240 }
241
242 private void hostUpdated(Host host) {
243 if (dhcpGatewayIp != null) {
244 if (host.ipAddresses().contains(dhcpGatewayIp)) {
245 dhcpConnectMac = host.mac();
246 dhcpConnectVlan = host.vlan();
247 log.info("DHCP gateway {} resolved to Mac/Vlan:{}/{}", dhcpGatewayIp,
248 dhcpConnectMac, dhcpConnectVlan);
249 }
250 return;
251 }
252 if (host.ipAddresses().contains(dhcpServerIp)) {
253 dhcpConnectMac = host.mac();
254 dhcpConnectVlan = host.vlan();
255 log.info("DHCP server {} resolved to Mac/Vlan:{}/{}", dhcpServerIp,
256 dhcpConnectMac, dhcpConnectVlan);
257 }
gauravf0884562016-06-17 02:47:13 +0530258 }
259
260 /**
Pier Luigi20557c82017-01-12 22:40:46 -0800261 * Request DHCP packet in via PacketService.
gauravf0884562016-06-17 02:47:13 +0530262 */
Pier Luigi20557c82017-01-12 22:40:46 -0800263 private void requestDhcpPackets() {
gauravf0884562016-06-17 02:47:13 +0530264 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
265 .matchEthType(Ethernet.TYPE_IPV4)
266 .matchIPProtocol(IPv4.PROTOCOL_UDP)
gaurav38351de2016-07-27 04:44:33 +0530267 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT));
gauravf0884562016-06-17 02:47:13 +0530268 packetService.requestPackets(selectorServer.build(), PacketPriority.CONTROL, appId);
269
270 TrafficSelector.Builder selectorClient = DefaultTrafficSelector.builder()
271 .matchEthType(Ethernet.TYPE_IPV4)
272 .matchIPProtocol(IPv4.PROTOCOL_UDP)
gaurav38351de2016-07-27 04:44:33 +0530273 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
gauravf0884562016-06-17 02:47:13 +0530274 packetService.requestPackets(selectorClient.build(), PacketPriority.CONTROL, appId);
275 }
276
277 /**
Pier Luigi20557c82017-01-12 22:40:46 -0800278 * Cancel requested DHCP packets in via packet service.
gauravf0884562016-06-17 02:47:13 +0530279 */
Pier Luigi20557c82017-01-12 22:40:46 -0800280 private void cancelDhcpPackets() {
gauravf0884562016-06-17 02:47:13 +0530281 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
282 .matchEthType(Ethernet.TYPE_IPV4)
283 .matchIPProtocol(IPv4.PROTOCOL_UDP)
gaurav38351de2016-07-27 04:44:33 +0530284 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT));
gauravf0884562016-06-17 02:47:13 +0530285 packetService.cancelPackets(selectorServer.build(), PacketPriority.CONTROL, appId);
286
287 TrafficSelector.Builder selectorClient = DefaultTrafficSelector.builder()
288 .matchEthType(Ethernet.TYPE_IPV4)
289 .matchIPProtocol(IPv4.PROTOCOL_UDP)
gaurav38351de2016-07-27 04:44:33 +0530290 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
gauravf0884562016-06-17 02:47:13 +0530291 packetService.cancelPackets(selectorClient.build(), PacketPriority.CONTROL, appId);
Pier Luigi20557c82017-01-12 22:40:46 -0800292 }
gaurav38351de2016-07-27 04:44:33 +0530293
Pier Luigi20557c82017-01-12 22:40:46 -0800294 /**
295 * Request ARP packet in via PacketService.
296 */
297 private void requestArpPackets() {
298 TrafficSelector.Builder selectorArpServer = DefaultTrafficSelector.builder()
299 .matchEthType(Ethernet.TYPE_ARP);
300 packetService.requestPackets(selectorArpServer.build(), PacketPriority.CONTROL, appId);
301 }
302
303 /**
304 * Cancel requested ARP packets in via packet service.
305 */
306 private void cancelArpPackets() {
gaurav38351de2016-07-27 04:44:33 +0530307 TrafficSelector.Builder selectorArpServer = DefaultTrafficSelector.builder()
308 .matchEthType(Ethernet.TYPE_ARP);
309 packetService.cancelPackets(selectorArpServer.build(), PacketPriority.CONTROL, appId);
gauravf0884562016-06-17 02:47:13 +0530310 }
311
312 private class DhcpRelayPacketProcessor implements PacketProcessor {
313
314 @Override
315 public void process(PacketContext context) {
Charles Chan3c1691a2017-02-16 14:55:22 -0800316 if (!configured()) {
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800317 log.warn("Missing DHCP relay server config. Abort packet processing");
Charles Chan3c1691a2017-02-16 14:55:22 -0800318 return;
319 }
320
gauravf0884562016-06-17 02:47:13 +0530321 // process the packet and get the payload
322 Ethernet packet = context.inPacket().parsed();
gauravf0884562016-06-17 02:47:13 +0530323 if (packet == null) {
324 return;
325 }
326
327 if (packet.getEtherType() == Ethernet.TYPE_IPV4) {
328 IPv4 ipv4Packet = (IPv4) packet.getPayload();
329
330 if (ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
331 UDP udpPacket = (UDP) ipv4Packet.getPayload();
332 DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
gaurav38351de2016-07-27 04:44:33 +0530333 if (udpPacket.getSourcePort() == UDP.DHCP_CLIENT_PORT ||
334 udpPacket.getSourcePort() == UDP.DHCP_SERVER_PORT) {
gaurav4af95fb2016-07-07 01:48:44 +0530335 //This packet is dhcp.
336 processDhcpPacket(context, dhcpPayload);
gauravf0884562016-06-17 02:47:13 +0530337 }
338 }
Pier Luigi20557c82017-01-12 22:40:46 -0800339 } else if (packet.getEtherType() == Ethernet.TYPE_ARP && arpEnabled) {
gaurav38351de2016-07-27 04:44:33 +0530340 ARP arpPacket = (ARP) packet.getPayload();
341 Set<Interface> serverInterfaces = interfaceService.
342 getInterfacesByPort(context.inPacket().receivedFrom());
343 //ignore the packets if dhcp server interface is not configured on onos.
344 if (serverInterfaces.isEmpty()) {
345 log.warn("server virtual interface not configured");
346 return;
347 }
348 if ((arpPacket.getOpCode() == ARP.OP_REQUEST) &&
349 checkArpRequestFrmDhcpServ(serverInterfaces, arpPacket)) {
350 processArpPacket(context, packet);
351 }
gauravf0884562016-06-17 02:47:13 +0530352 }
353 }
354
gaurav38351de2016-07-27 04:44:33 +0530355 //method to check the arp request is from dhcp server for default-gateway.
356 private boolean checkArpRequestFrmDhcpServ(Set<Interface> serverInterfaces, ARP arpPacket) {
357 if (Objects.equals(serverInterfaces.iterator().next().ipAddressesList().get(0).
358 ipAddress().getIp4Address(),
359 Ip4Address.valueOf(arpPacket.getTargetProtocolAddress()))) {
360 return true;
361 }
362 return false;
363 }
364
gauravf0884562016-06-17 02:47:13 +0530365 //forward the packet to ConnectPoint where the DHCP server is attached.
gaurav4af95fb2016-07-07 01:48:44 +0530366 private void forwardPacket(Ethernet packet) {
gauravf0884562016-06-17 02:47:13 +0530367 //send Packetout to dhcp server connectpoint.
368 if (dhcpServerConnectPoint != null) {
369 TrafficTreatment t = DefaultTrafficTreatment.builder()
370 .setOutput(dhcpServerConnectPoint.port()).build();
371 OutboundPacket o = new DefaultOutboundPacket(
gaurav4af95fb2016-07-07 01:48:44 +0530372 dhcpServerConnectPoint.deviceId(), t, ByteBuffer.wrap(packet.serialize()));
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800373 if (log.isTraceEnabled()) {
374 log.trace("Relaying packet to dhcp server {}", packet);
375 }
gauravf0884562016-06-17 02:47:13 +0530376 packetService.emit(o);
377 }
378 }
379
gaurav38351de2016-07-27 04:44:33 +0530380 /**
381 * Processes the ARP Payload and initiates a reply to the client.
382 *
383 * @param context context of the incoming message
384 * @param packet the ethernet payload
385 */
386 private void processArpPacket(PacketContext context, Ethernet packet) {
gaurav38351de2016-07-27 04:44:33 +0530387 ARP arpPacket = (ARP) packet.getPayload();
388
389 ARP arpReply = (ARP) arpPacket.clone();
390 arpReply.setOpCode(ARP.OP_REPLY);
391
392 arpReply.setTargetProtocolAddress(arpPacket.getSenderProtocolAddress());
393 arpReply.setTargetHardwareAddress(arpPacket.getSenderHardwareAddress());
394 arpReply.setSenderProtocolAddress(arpPacket.getTargetProtocolAddress());
395 arpReply.setSenderHardwareAddress(myMAC.toBytes());
396
397 // Ethernet Frame.
398 Ethernet ethReply = new Ethernet();
399 ethReply.setSourceMACAddress(myMAC);
400 ethReply.setDestinationMACAddress(packet.getSourceMAC());
401 ethReply.setEtherType(Ethernet.TYPE_ARP);
402 ethReply.setVlanID(packet.getVlanID());
403
404 ethReply.setPayload(arpReply);
405 forwardPacket(ethReply);
406 }
407
gaurav4af95fb2016-07-07 01:48:44 +0530408 //process the dhcp packet before sending to server
gauravf0884562016-06-17 02:47:13 +0530409 private void processDhcpPacket(PacketContext context, DHCP dhcpPayload) {
Charles Chan1b201232016-12-07 15:39:22 -0800410 ConnectPoint inPort = context.inPacket().receivedFrom();
411 Set<Interface> clientServerInterfaces = interfaceService.getInterfacesByPort(inPort);
gaurav38351de2016-07-27 04:44:33 +0530412 //ignore the packets if dhcp client interface is not configured on onos.
413 if (clientServerInterfaces.isEmpty()) {
Charles Chan1b201232016-12-07 15:39:22 -0800414 log.warn("Virtual interface is not configured on {}", inPort);
gaurav38351de2016-07-27 04:44:33 +0530415 return;
416 }
417
gauravf0884562016-06-17 02:47:13 +0530418 if (dhcpPayload == null) {
419 return;
420 }
gaurav4af95fb2016-07-07 01:48:44 +0530421
gauravf0884562016-06-17 02:47:13 +0530422 Ethernet packet = context.inPacket().parsed();
Yi Tsengc7403c22017-06-19 16:23:22 -0700423 DHCP.MsgType incomingPacketType = null;
424 for (DhcpOption option : dhcpPayload.getOptions()) {
gauravf0884562016-06-17 02:47:13 +0530425 if (option.getCode() == OptionCode_MessageType.getValue()) {
426 byte[] data = option.getData();
Yi Tsengc7403c22017-06-19 16:23:22 -0700427 incomingPacketType = DHCP.MsgType.getType(data[0]);
gauravf0884562016-06-17 02:47:13 +0530428 }
429 }
gaurav38351de2016-07-27 04:44:33 +0530430
gauravf0884562016-06-17 02:47:13 +0530431 switch (incomingPacketType) {
432 case DHCPDISCOVER:
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800433 // add the gatewayip as virtual interface ip for server to understand
434 // the lease to be assigned and forward the packet to dhcp server.
435 Ethernet ethernetPacketDiscover =
436 processDhcpPacketFromClient(context, packet, clientServerInterfaces);
Charles Chan3c1691a2017-02-16 14:55:22 -0800437 if (ethernetPacketDiscover != null) {
438 forwardPacket(ethernetPacketDiscover);
439 }
gaurav4af95fb2016-07-07 01:48:44 +0530440 break;
441 case DHCPOFFER:
442 //reply to dhcp client.
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800443 Ethernet ethernetPacketOffer = processDhcpPacketFromServer(packet);
Charles Chan3c1691a2017-02-16 14:55:22 -0800444 if (ethernetPacketOffer != null) {
445 sendReply(ethernetPacketOffer, dhcpPayload);
446 }
gaurav4af95fb2016-07-07 01:48:44 +0530447 break;
448 case DHCPREQUEST:
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800449 // add the gatewayip as virtual interface ip for server to understand
450 // the lease to be assigned and forward the packet to dhcp server.
451 Ethernet ethernetPacketRequest =
452 processDhcpPacketFromClient(context, packet, clientServerInterfaces);
Charles Chan3c1691a2017-02-16 14:55:22 -0800453 if (ethernetPacketRequest != null) {
454 forwardPacket(ethernetPacketRequest);
455 }
gaurav4af95fb2016-07-07 01:48:44 +0530456 break;
457 case DHCPACK:
458 //reply to dhcp client.
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800459 Ethernet ethernetPacketAck = processDhcpPacketFromServer(packet);
Charles Chan3c1691a2017-02-16 14:55:22 -0800460 if (ethernetPacketAck != null) {
461 sendReply(ethernetPacketAck, dhcpPayload);
462 }
gauravf0884562016-06-17 02:47:13 +0530463 break;
464 default:
465 break;
466 }
gaurav4af95fb2016-07-07 01:48:44 +0530467 }
468
gaurav38351de2016-07-27 04:44:33 +0530469 //build the DHCP discover/request packet with gatewayip(unicast packet)
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800470 private Ethernet processDhcpPacketFromClient(PacketContext context,
471 Ethernet ethernetPacket, Set<Interface> clientInterfaces) {
472 Ip4Address relayAgentIp = getRelayAgentIPv4Address(clientInterfaces);
473 MacAddress relayAgentMac = clientInterfaces.iterator().next().mac();
474 if (relayAgentIp == null || relayAgentMac == null) {
475 log.warn("Missing DHCP relay agent interface Ipv4 addr config for "
476 + "packet from client on port: {}. Aborting packet processing",
477 clientInterfaces.iterator().next().connectPoint());
Charles Chan3c1691a2017-02-16 14:55:22 -0800478 return null;
479 }
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800480 if (dhcpConnectMac == null) {
481 log.warn("DHCP {} not yet resolved .. Aborting DHCP "
482 + "packet processing from client on port: {}",
483 (dhcpGatewayIp == null) ? "server IP " + dhcpServerIp
484 : "gateway IP " + dhcpGatewayIp,
485 clientInterfaces.iterator().next().connectPoint());
486 return null;
487 }
gaurav4af95fb2016-07-07 01:48:44 +0530488 // get dhcp header.
489 Ethernet etherReply = (Ethernet) ethernetPacket.clone();
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800490 etherReply.setSourceMACAddress(relayAgentMac);
491 etherReply.setDestinationMACAddress(dhcpConnectMac);
492 etherReply.setVlanID(dhcpConnectVlan.toShort());
gaurav4af95fb2016-07-07 01:48:44 +0530493 IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800494 ipv4Packet.setSourceAddress(relayAgentIp.toInt());
gaurav38351de2016-07-27 04:44:33 +0530495 ipv4Packet.setDestinationAddress(dhcpServerIp.toInt());
gaurav4af95fb2016-07-07 01:48:44 +0530496 UDP udpPacket = (UDP) ipv4Packet.getPayload();
497 DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800498 dhcpPacket.setGatewayIPAddress(relayAgentIp.toInt());
gaurav4af95fb2016-07-07 01:48:44 +0530499 udpPacket.setPayload(dhcpPacket);
500 ipv4Packet.setPayload(udpPacket);
501 etherReply.setPayload(ipv4Packet);
502 return etherReply;
503 }
gauravf0884562016-06-17 02:47:13 +0530504
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800505 // Returns the first v4 interface ip out of a set of interfaces or null.
506 // Checks all interfaces, and ignores v6 interface ips
507 private Ip4Address getRelayAgentIPv4Address(Set<Interface> intfs) {
508 for (Interface intf : intfs) {
509 for (InterfaceIpAddress ip : intf.ipAddressesList()) {
510 Ip4Address relayAgentIp = ip.ipAddress().getIp4Address();
511 if (relayAgentIp != null) {
512 return relayAgentIp;
513 }
514 }
Charles Chan3c1691a2017-02-16 14:55:22 -0800515 }
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800516 return null;
517 }
Charles Chan3c1691a2017-02-16 14:55:22 -0800518
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800519 //build the DHCP offer/ack with proper client port.
520 private Ethernet processDhcpPacketFromServer(Ethernet ethernetPacket) {
gaurav38351de2016-07-27 04:44:33 +0530521 // get dhcp header.
522 Ethernet etherReply = (Ethernet) ethernetPacket.clone();
523 IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
524 UDP udpPacket = (UDP) ipv4Packet.getPayload();
525 DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800526
527 // determine the vlanId of the client host - note that this vlan id
528 // could be different from the vlan in the packet from the server
529 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 "
533 + "relay for dhcp packet from server {}",
534 dhcpPayload.getClientHardwareAddress(), ethernetPacket);
535 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 etherReply.setDestinationMACAddress(dstMac);
543 etherReply.setVlanID(host.vlan().toShort());
544 // we leave the srcMac from the original packet
545
546 // figure out the relay agent IP corresponding to the original request
547 Ip4Address relayAgentIP = getRelayAgentIPv4Address(
548 interfaceService.getInterfacesByPort(host.location()));
549 if (relayAgentIP == null) {
550 log.warn("Cannot determine relay agent interface Ipv4 addr for host {}. "
551 + "Aborting relay for dhcp packet from server {}",
552 host, ethernetPacket);
553 return null;
554 }
Charles Chanda5dc8c2017-01-13 14:29:29 -0800555 // SRC_IP: relay agent IP
556 // DST_IP: offered IP
557 ipv4Packet.setSourceAddress(relayAgentIP.toInt());
558 ipv4Packet.setDestinationAddress(dhcpPayload.getYourIPAddress());
559
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800560 udpPacket.setDestinationPort(UDP.DHCP_CLIENT_PORT);
gaurav38351de2016-07-27 04:44:33 +0530561 udpPacket.setPayload(dhcpPayload);
562 ipv4Packet.setPayload(udpPacket);
563 etherReply.setPayload(ipv4Packet);
564 return etherReply;
565 }
566
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800567 //send the response to the requester host.
gaurav38351de2016-07-27 04:44:33 +0530568 private void sendReply(Ethernet ethPacket, DHCP dhcpPayload) {
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800569 MacAddress descMac = valueOf(dhcpPayload.getClientHardwareAddress());
gaurav38351de2016-07-27 04:44:33 +0530570 Host host = hostService.getHost(HostId.hostId(descMac,
gaurav4af95fb2016-07-07 01:48:44 +0530571 VlanId.vlanId(ethPacket.getVlanID())));
gauravf0884562016-06-17 02:47:13 +0530572
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800573 // Send packet out to requester if the host information is available
Charles Chan3c1691a2017-02-16 14:55:22 -0800574 if (host != null) {
gauravf0884562016-06-17 02:47:13 +0530575 TrafficTreatment t = DefaultTrafficTreatment.builder()
Charles Chan3c1691a2017-02-16 14:55:22 -0800576 .setOutput(host.location().port()).build();
gauravf0884562016-06-17 02:47:13 +0530577 OutboundPacket o = new DefaultOutboundPacket(
Charles Chan3c1691a2017-02-16 14:55:22 -0800578 host.location().deviceId(), t, ByteBuffer.wrap(ethPacket.serialize()));
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800579 if (log.isTraceEnabled()) {
580 log.trace("Relaying packet to dhcp client {}", ethPacket);
581 }
gauravf0884562016-06-17 02:47:13 +0530582 packetService.emit(o);
583 }
584 }
585 }
586
587 /**
588 * Listener for network config events.
589 */
590 private class InternalConfigListener implements NetworkConfigListener {
591
592 @Override
593 public void event(NetworkConfigEvent event) {
594
595 if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
596 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) &&
597 event.configClass().equals(DhcpRelayConfig.class)) {
598 updateConfig();
599 log.info("Reconfigured");
600 }
601 }
602 }
Saurav Dasb0ae6ee2017-03-04 16:08:47 -0800603
604 /**
605 * Internal listener for host events.
606 */
607 private class InternalHostListener implements HostListener {
608 @Override
609 public void event(HostEvent event) {
610 switch (event.type()) {
611 case HOST_ADDED:
612 case HOST_UPDATED:
613 hostUpdated(event.subject());
614 break;
615 case HOST_REMOVED:
616 hostRemoved(event.subject());
617 break;
618 case HOST_MOVED:
619 // XXX todo -- moving dhcp server
620 break;
621 default:
622 break;
623 }
624 }
625 }
Lei Xu3aeaf722016-08-11 13:51:12 -0700626}