blob: 79bfaa85e09afd2df5641f23fd8518129739dbe6 [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;
19import java.util.List;
gauravf0884562016-06-17 02:47:13 +053020import java.util.Set;
21
22import 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.Reference;
26import org.apache.felix.scr.annotations.ReferenceCardinality;
27import org.onlab.packet.DHCP;
gaurav4af95fb2016-07-07 01:48:44 +053028import org.onlab.packet.DHCPOption;
29import org.onlab.packet.DHCPPacketType;
gauravf0884562016-06-17 02:47:13 +053030import org.onlab.packet.Ethernet;
31import org.onlab.packet.IPv4;
32import org.onlab.packet.TpPort;
33import org.onlab.packet.UDP;
gaurav4af95fb2016-07-07 01:48:44 +053034import org.onlab.packet.VlanId;
35import org.onlab.util.HexString;
gauravf0884562016-06-17 02:47:13 +053036import org.onosproject.core.ApplicationId;
37import org.onosproject.core.CoreService;
38import org.onosproject.net.ConnectPoint;
39import org.onosproject.net.Host;
40import org.onosproject.net.HostId;
41import org.onosproject.net.config.ConfigFactory;
42import org.onosproject.net.config.NetworkConfigEvent;
43import org.onosproject.net.config.NetworkConfigListener;
44import org.onosproject.net.config.NetworkConfigRegistry;
45import org.onosproject.net.flow.DefaultTrafficSelector;
46import org.onosproject.net.flow.DefaultTrafficTreatment;
47import org.onosproject.net.flow.TrafficSelector;
48import org.onosproject.net.flow.TrafficTreatment;
49import org.onosproject.net.host.HostService;
50import org.onosproject.net.packet.DefaultOutboundPacket;
51import org.onosproject.net.packet.OutboundPacket;
52import org.onosproject.net.packet.PacketContext;
53import org.onosproject.net.packet.PacketPriority;
54import org.onosproject.net.packet.PacketProcessor;
55import org.onosproject.net.packet.PacketService;
56import org.slf4j.Logger;
57import org.slf4j.LoggerFactory;
58
59import com.google.common.collect.ImmutableSet;
60import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
gaurav4af95fb2016-07-07 01:48:44 +053061import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_CircuitID;
62import static org.onlab.packet.DHCP.DHCPOptionCode.OptionCode_MessageType;
gauravf0884562016-06-17 02:47:13 +053063/**
64 * DHCP Relay Agent Application Component.
65 */
66@Component(immediate = true)
67public class DhcpRelay {
68
69 public static final String DHCP_RELAY_APP = "org.onosproject.dhcp-relay";
70 private final Logger log = LoggerFactory.getLogger(getClass());
71 private final InternalConfigListener cfgListener = new InternalConfigListener();
72
73 private final Set<ConfigFactory> factories = ImmutableSet.of(
74 new ConfigFactory<ApplicationId, DhcpRelayConfig>(APP_SUBJECT_FACTORY,
75 DhcpRelayConfig.class,
76 "dhcprelay") {
77 @Override
78 public DhcpRelayConfig createConfig() {
79 return new DhcpRelayConfig();
80 }
81 }
82 );
83
84 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
85 protected NetworkConfigRegistry cfgService;
86
87 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
88 protected CoreService coreService;
89
90 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
91 protected PacketService packetService;
92
93 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
94 protected HostService hostService;
95
96 private DhcpRelayPacketProcessor dhcpRelayPacketProcessor = new DhcpRelayPacketProcessor();
97 private ConnectPoint dhcpServerConnectPoint = null;
98 private ApplicationId appId;
99
100 @Activate
101 protected void activate() {
102 //start the dhcp relay agent
103
104 appId = coreService.registerApplication(DHCP_RELAY_APP);
105
106 cfgService.addListener(cfgListener);
107 factories.forEach(cfgService::registerConfigFactory);
108 //update the dhcp server configuration.
109 updateConfig();
110 //add the packet services.
111 packetService.addProcessor(dhcpRelayPacketProcessor, PacketProcessor.director(0));
112 requestPackets();
113 log.info("DHCP-RELAY Started");
114 log.info("started the apps dhcp relay");
115 }
116
117 @Deactivate
118 protected void deactivate() {
119 cfgService.removeListener(cfgListener);
120 factories.forEach(cfgService::unregisterConfigFactory);
121 packetService.removeProcessor(dhcpRelayPacketProcessor);
122 cancelPackets();
123 log.info("DHCP-RELAY Stopped");
124 }
125
126 private void updateConfig() {
127 DhcpRelayConfig cfg = cfgService.getConfig(appId, DhcpRelayConfig.class);
128
129 if (cfg == null) {
130 log.warn("Dhcp Server info not available");
131 return;
132 }
133
134 dhcpServerConnectPoint = cfg.getDhcpServerConnectPoint();
135 log.info("Reconfigured the dhcp server info");
136 log.info("dhcp server connect points are " + dhcpServerConnectPoint);
137 }
138
139 /**
140 * Request packet in via PacketService.
141 */
142 private void requestPackets() {
143
144 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
145 .matchEthType(Ethernet.TYPE_IPV4)
146 .matchIPProtocol(IPv4.PROTOCOL_UDP)
147 .matchUdpDst(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
148 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
149 packetService.requestPackets(selectorServer.build(), PacketPriority.CONTROL, appId);
150
151 TrafficSelector.Builder selectorClient = DefaultTrafficSelector.builder()
152 .matchEthType(Ethernet.TYPE_IPV4)
153 .matchIPProtocol(IPv4.PROTOCOL_UDP)
154 .matchUdpDst(TpPort.tpPort(UDP.DHCP_CLIENT_PORT))
155 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT));
156 packetService.requestPackets(selectorClient.build(), PacketPriority.CONTROL, appId);
157 }
158
159 /**
160 * Cancel requested packets in via packet service.
161 */
162 private void cancelPackets() {
163 TrafficSelector.Builder selectorServer = DefaultTrafficSelector.builder()
164 .matchEthType(Ethernet.TYPE_IPV4)
165 .matchIPProtocol(IPv4.PROTOCOL_UDP)
166 .matchUdpDst(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
167 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT));
168 packetService.cancelPackets(selectorServer.build(), PacketPriority.CONTROL, appId);
169
170 TrafficSelector.Builder selectorClient = DefaultTrafficSelector.builder()
171 .matchEthType(Ethernet.TYPE_IPV4)
172 .matchIPProtocol(IPv4.PROTOCOL_UDP)
173 .matchUdpDst(TpPort.tpPort(UDP.DHCP_CLIENT_PORT))
174 .matchUdpSrc(TpPort.tpPort(UDP.DHCP_SERVER_PORT));
175 packetService.cancelPackets(selectorClient.build(), PacketPriority.CONTROL, appId);
176 }
177
178 private class DhcpRelayPacketProcessor implements PacketProcessor {
179
180 @Override
181 public void process(PacketContext context) {
182 // process the packet and get the payload
183 Ethernet packet = context.inPacket().parsed();
184
185 if (packet == null) {
186 return;
187 }
188
189 if (packet.getEtherType() == Ethernet.TYPE_IPV4) {
190 IPv4 ipv4Packet = (IPv4) packet.getPayload();
191
192 if (ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
193 UDP udpPacket = (UDP) ipv4Packet.getPayload();
194 DHCP dhcpPayload = (DHCP) udpPacket.getPayload();
195 if (udpPacket.getDestinationPort() == UDP.DHCP_SERVER_PORT &&
gaurav4af95fb2016-07-07 01:48:44 +0530196 udpPacket.getSourcePort() == UDP.DHCP_CLIENT_PORT ||
197 udpPacket.getSourcePort() == UDP.DHCP_SERVER_PORT &&
198 udpPacket.getDestinationPort() == UDP.DHCP_CLIENT_PORT) {
199 //This packet is dhcp.
200 processDhcpPacket(context, dhcpPayload);
gauravf0884562016-06-17 02:47:13 +0530201 }
202 }
203 }
204 }
205
206 //forward the packet to ConnectPoint where the DHCP server is attached.
gaurav4af95fb2016-07-07 01:48:44 +0530207 private void forwardPacket(Ethernet packet) {
gauravf0884562016-06-17 02:47:13 +0530208
209 //send Packetout to dhcp server connectpoint.
210 if (dhcpServerConnectPoint != null) {
211 TrafficTreatment t = DefaultTrafficTreatment.builder()
212 .setOutput(dhcpServerConnectPoint.port()).build();
213 OutboundPacket o = new DefaultOutboundPacket(
gaurav4af95fb2016-07-07 01:48:44 +0530214 dhcpServerConnectPoint.deviceId(), t, ByteBuffer.wrap(packet.serialize()));
gauravf0884562016-06-17 02:47:13 +0530215 packetService.emit(o);
216 }
217 }
218
gaurav4af95fb2016-07-07 01:48:44 +0530219 //process the dhcp packet before sending to server
gauravf0884562016-06-17 02:47:13 +0530220 private void processDhcpPacket(PacketContext context, DHCP dhcpPayload) {
gaurav4af95fb2016-07-07 01:48:44 +0530221
gauravf0884562016-06-17 02:47:13 +0530222 if (dhcpPayload == null) {
223 return;
224 }
gaurav4af95fb2016-07-07 01:48:44 +0530225
gauravf0884562016-06-17 02:47:13 +0530226 Ethernet packet = context.inPacket().parsed();
gaurav4af95fb2016-07-07 01:48:44 +0530227 String circuitIdFrmClient = context.inPacket().receivedFrom().elementId().toString();
gauravf0884562016-06-17 02:47:13 +0530228 DHCPPacketType incomingPacketType = null;
229 for (DHCPOption option : dhcpPayload.getOptions()) {
230 if (option.getCode() == OptionCode_MessageType.getValue()) {
231 byte[] data = option.getData();
232 incomingPacketType = DHCPPacketType.getType(data[0]);
233 }
234 }
235 switch (incomingPacketType) {
236 case DHCPDISCOVER:
gaurav4af95fb2016-07-07 01:48:44 +0530237 //add the circuit id as switch dpid and forward the packet to dhcp server.
238 Ethernet ethernetPacketDiscover = processDhcpPacketFrmClient(packet, circuitIdFrmClient,
239 (byte) DHCPPacketType.DHCPDISCOVER.getValue());
240 forwardPacket(ethernetPacketDiscover);
241 break;
242 case DHCPOFFER:
243 //reply to dhcp client.
244 sendReply(packet);
245 break;
246 case DHCPREQUEST:
247 //add the circuit id as switch dpid and forward the packet to dhcp server.
248 Ethernet ethernetPacketRequest = processDhcpPacketFrmClient(packet, circuitIdFrmClient,
249 (byte) DHCPPacketType.DHCPREQUEST.getValue());
250 forwardPacket(ethernetPacketRequest);
251 break;
252 case DHCPACK:
253 //reply to dhcp client.
254 sendReply(packet);
gauravf0884562016-06-17 02:47:13 +0530255 break;
256 default:
257 break;
258 }
gaurav4af95fb2016-07-07 01:48:44 +0530259 }
260
261 //build the DHCP discover/request packet with circuitid(DpId) suboption.
262 private Ethernet processDhcpPacketFrmClient(Ethernet ethernetPacket, String circuitId,
263 byte incomingPacketType) {
264
265 // get dhcp header.
266 Ethernet etherReply = (Ethernet) ethernetPacket.clone();
267 IPv4 ipv4Packet = (IPv4) etherReply.getPayload();
268 UDP udpPacket = (UDP) ipv4Packet.getPayload();
269 DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
270
271 // DHCP Options.
272 List<DHCPOption> optionList = dhcpPacket.getOptions();
273
274 // Dhcp SubOption as CircuitID
275 DHCPOption option = new DHCPOption();
276 option.setCode(OptionCode_CircuitID.getValue());
277 option.setLength((byte) 10);
278
279 // start object for suboption circuit id.
280 String[] actualDpId = circuitId.split(":", 2);
281 DHCPOption subOption = new DHCPOption();
282 subOption.setCode((byte) 1);
283 byte[] subOptionData = HexString.fromHexString(actualDpId[1], null);
284 subOption.setData(subOptionData);
285 subOption.setLength((byte) 8);
286 // end object for suboption circuit id.
287
288 // converting suboption to byte array
289 byte[] data = new byte[10];
290 ByteBuffer bb = ByteBuffer.wrap(data);
291 DHCP.dhcpOptionToByteArray(subOption, bb);
292
293 option.setData(data);
294 optionList.add(optionList.size() - 1, option);
295
296 dhcpPacket.setOptions(optionList);
297 udpPacket.setPayload(dhcpPacket);
298 ipv4Packet.setPayload(udpPacket);
299 etherReply.setPayload(ipv4Packet);
300 return etherReply;
301 }
gauravf0884562016-06-17 02:47:13 +0530302
303 //send the response to the requestor host.
gaurav4af95fb2016-07-07 01:48:44 +0530304 private void sendReply(Ethernet ethPacket) {
305
gauravf0884562016-06-17 02:47:13 +0530306 //get the host info
gaurav4af95fb2016-07-07 01:48:44 +0530307 Host host = hostService.getHost(HostId.hostId(ethPacket.getDestinationMAC(),
308 VlanId.vlanId(ethPacket.getVlanID())));
gauravf0884562016-06-17 02:47:13 +0530309 ConnectPoint dhcpRequestor = new ConnectPoint(host.location().elementId(),
310 host.location().port());
311
312 //send Packetout to requestor host.
313 if (dhcpRequestor != null) {
314 TrafficTreatment t = DefaultTrafficTreatment.builder()
315 .setOutput(dhcpRequestor.port()).build();
316 OutboundPacket o = new DefaultOutboundPacket(
gaurav4af95fb2016-07-07 01:48:44 +0530317 dhcpRequestor.deviceId(), t, ByteBuffer.wrap(ethPacket.serialize()));
gauravf0884562016-06-17 02:47:13 +0530318 packetService.emit(o);
319 }
320 }
321 }
322
323 /**
324 * Listener for network config events.
325 */
326 private class InternalConfigListener implements NetworkConfigListener {
327
328 @Override
329 public void event(NetworkConfigEvent event) {
330
331 if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
332 event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) &&
333 event.configClass().equals(DhcpRelayConfig.class)) {
334 updateConfig();
335 log.info("Reconfigured");
336 }
337 }
338 }
339}