Update to work with Aluminium GA
[l2switch.git] / packethandler / implementation / src / main / java / org / opendaylight / l2switch / packethandler / decoders / EthernetDecoder.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.l2switch.packethandler.decoders;
9
10 import java.util.ArrayList;
11 import org.opendaylight.l2switch.packethandler.decoders.utils.BitBufferHelper;
12 import org.opendaylight.l2switch.packethandler.decoders.utils.BufferException;
13 import org.opendaylight.l2switch.packethandler.decoders.utils.HexEncode;
14 import org.opendaylight.l2switch.packethandler.decoders.utils.NetUtils;
15 import org.opendaylight.mdsal.binding.api.NotificationPublishService;
16 import org.opendaylight.mdsal.binding.api.NotificationService;
17 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
18 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.basepacket.rev140528.packet.chain.grp.PacketChain;
19 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.basepacket.rev140528.packet.chain.grp.PacketChainBuilder;
20 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.basepacket.rev140528.packet.chain.grp.packet.chain.packet.RawPacketBuilder;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.basepacket.rev140528.packet.chain.grp.packet.chain.packet.raw.packet.RawPacketFieldsBuilder;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.basepacket.rev140528.packet.chain.grp.packet.chain.packet.raw.packet.raw.packet.fields.MatchBuilder;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.ethernet.rev140528.EthernetPacketReceived;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.ethernet.rev140528.EthernetPacketReceivedBuilder;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.ethernet.rev140528.Header8021qType;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.ethernet.rev140528.KnownEtherType;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.ethernet.rev140528.VlanId;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.ethernet.rev140528.ethernet.packet.fields.Header8021q;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.ethernet.rev140528.ethernet.packet.fields.Header8021qBuilder;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.ethernet.rev140528.ethernet.packet.received.packet.chain.packet.EthernetPacketBuilder;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketProcessingListener;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived;
33 import org.opendaylight.yangtools.yang.binding.NotificationListener;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37 /**
38  * Ethernet Packet Decoder.
39  */
40 public class EthernetDecoder extends AbstractPacketDecoder<PacketReceived, EthernetPacketReceived>
41         implements PacketProcessingListener {
42     private static final Logger LOG = LoggerFactory.getLogger(EthernetDecoder.class);
43     public static final Integer LENGTH_MAX = 1500;
44     public static final Integer ETHERTYPE_MIN = 1536;
45     public static final Integer ETHERTYPE_8021Q = 0x8100;
46     public static final Integer ETHERTYPE_QINQ = 0x9100;
47
48     public EthernetDecoder(NotificationPublishService notificationProviderService,
49                            NotificationService notificationService) {
50         super(EthernetPacketReceived.class, notificationProviderService, notificationService);
51     }
52
53     @Override
54     public void onPacketReceived(PacketReceived packetReceived) {
55         decodeAndPublish(packetReceived);
56     }
57
58     /**
59      * Decode a RawPacket into an EthernetPacket.
60      *
61      * @param packetReceived
62      *            -- data from wire to deserialize
63      * @return EthernetPacketReceived
64      */
65     @Override
66     public EthernetPacketReceived decode(PacketReceived packetReceived) {
67         byte[] data = packetReceived.getPayload();
68         EthernetPacketReceivedBuilder builder = new EthernetPacketReceivedBuilder();
69
70         // Save original rawPacket & set the payloadOffset/payloadLength fields
71         RawPacketFieldsBuilder rpfb = new RawPacketFieldsBuilder().setIngress(packetReceived.getIngress())
72                 .setConnectionCookie(packetReceived.getConnectionCookie()).setFlowCookie(packetReceived.getFlowCookie())
73                 .setTableId(packetReceived.getTableId()).setPacketInReason(packetReceived.getPacketInReason())
74                 .setPayloadOffset(0).setPayloadLength(data.length);
75         if (packetReceived.getMatch() != null) {
76             rpfb.setMatch(new MatchBuilder(packetReceived.getMatch()).build());
77         }
78         ArrayList<PacketChain> packetChain = new ArrayList<>();
79         packetChain.add(new PacketChainBuilder()
80             .setPacket(new RawPacketBuilder().setRawPacketFields(rpfb.build()).build())
81             .build());
82
83         try {
84             EthernetPacketBuilder epBuilder = new EthernetPacketBuilder();
85
86             // Deserialize the destination & source fields
87             epBuilder.setDestinationMac(
88                 new MacAddress(HexEncode.bytesToHexStringFormat(BitBufferHelper.getBits(data, 0, 48))));
89             epBuilder.setSourceMac(
90                 new MacAddress(HexEncode.bytesToHexStringFormat(BitBufferHelper.getBits(data, 48, 48))));
91
92             // Deserialize the optional field 802.1Q headers
93             Integer nextField = BitBufferHelper.getInt(BitBufferHelper.getBits(data, 96, 16));
94             int extraHeaderBits = 0;
95             ArrayList<Header8021q> headerList = new ArrayList<>();
96             while (nextField.equals(ETHERTYPE_8021Q) || nextField.equals(ETHERTYPE_QINQ)) {
97                 Header8021qBuilder headerBuilder = new Header8021qBuilder();
98                 headerBuilder.setTPID(Header8021qType.forValue(nextField));
99
100                 // Read 2 more bytes for priority (3bits), drop eligible (1bit),
101                 // vlan-id (12bits)
102                 byte[] vlanBytes = BitBufferHelper.getBits(data, 112 + extraHeaderBits, 16);
103
104                 // Remove the sign & right-shift to get the priority code
105                 headerBuilder.setPriorityCode((short) ((vlanBytes[0] & 0xff) >> 5));
106
107                 // Remove the sign & remove priority code bits & right-shift to
108                 // get drop-eligible bit
109                 headerBuilder.setDropEligible(1 == (vlanBytes[0] & 0xff & 0x10) >> 4);
110
111                 // Remove priority code & drop-eligible bits, to get the VLAN-id
112                 vlanBytes[0] = (byte) (vlanBytes[0] & 0x0F);
113                 headerBuilder.setVlan(new VlanId(BitBufferHelper.getInt(vlanBytes)));
114
115                 // Add 802.1Q header to the growing collection
116                 headerList.add(headerBuilder.build());
117
118                 // Reset value of "nextField" to correspond to following 2 bytes
119                 // for next 802.1Q header or EtherType/Length
120                 nextField = BitBufferHelper.getInt(BitBufferHelper.getBits(data, 128 + extraHeaderBits, 16));
121
122                 // 802.1Q header means payload starts at a later position
123                 extraHeaderBits += 32;
124             }
125             // Set 802.1Q headers
126             if (!headerList.isEmpty()) {
127                 epBuilder.setHeader8021q(headerList);
128             }
129
130             // Deserialize the EtherType or Length field
131             if (nextField >= ETHERTYPE_MIN) {
132                 epBuilder.setEthertype(KnownEtherType.forValue(nextField));
133             } else if (nextField <= LENGTH_MAX) {
134                 epBuilder.setEthernetLength(nextField);
135             } else {
136                 LOG.debug("Undefined header, value is not valid EtherType or length.  Value is {}", nextField);
137             }
138
139             // Determine start & end of payload
140             int payloadStart = (112 + extraHeaderBits) / NetUtils.NUM_BITS_IN_A_BYTE;
141             int payloadEnd = data.length - 4;
142             epBuilder.setPayloadOffset(payloadStart);
143             epBuilder.setPayloadLength(payloadEnd - payloadStart);
144
145             // Deserialize the CRC
146             epBuilder.setCrc(BitBufferHelper
147                     .getLong(BitBufferHelper.getBits(data, (data.length - 4) * NetUtils.NUM_BITS_IN_A_BYTE, 32)));
148
149             // Set EthernetPacket field
150             packetChain.add(new PacketChainBuilder().setPacket(epBuilder.build()).build());
151
152             // Set Payload field
153             builder.setPayload(data);
154         } catch (BufferException be) {
155             LOG.info("Exception during decoding raw packet to ethernet.");
156         }
157
158         // ToDo: Possibly log these values
159         /*
160          * if (_logger.isTraceEnabled()) {
161          * _logger.trace("{}: {}: {} (offset {} bitsize {})", new Object[] {
162          * this.getClass().getSimpleName(), hdrField,
163          * HexEncode.bytesToHexString(hdrFieldBytes), startOffset, numBits }); }
164          */
165         builder.setPacketChain(packetChain);
166         return builder.build();
167     }
168
169     @Override
170     public NotificationListener getConsumedNotificationListener() {
171         return this;
172     }
173
174     @Override
175     public boolean canDecode(PacketReceived packetReceived) {
176         return packetReceived != null && packetReceived.getPayload() != null;
177     }
178 }