noxrepo / pox

The POX network software platform
https://noxrepo.github.io/pox-doc/html/
Apache License 2.0
620 stars 470 forks source link

Generation of Custom LLDP packet #230

Open joekutos opened 4 years ago

joekutos commented 4 years ago

The i am trying to create a custom LLPD packet by adding my own organisationally_specific TLV field. ` class organizationally_specific (simple_tlv): tlv_type = lldp.ORGANIZATIONALLY_SPECIFIC_TLV

def _init (self, kw): self.oui = '\x00\x00\x00' self.subtype = 0 self.payload = b''

def _parse_data (self, data): (self.oui,self.subtype) = struct.unpack("3sB", data[0:4]) self.payload = data[4:]

def _pack_data (self): return struct.pack('!3sB', self.oui, self.subtype) + self.payload My field just has a random UUID and a TIMESTAMP. what code changes should be done to be able to add these custom tlvs plus the <discovery.py > such that the added tlvs can be sent out in the packet out message during the discovery process. def create_packet_out (self, dpid, port_num, port_addr): """ Create an ofp_packet_out containing a discovery packet """ eth = self._create_discovery_packet(dpid, port_num, port_addr, self._ttl) po = of.ofp_packet_out(action = of.ofp_action_output(port=port_num)) po.data = eth.pack() return po.pack()

@staticmethod def _create_discovery_packet (dpid, port_num, port_addr, ttl): """ Build discovery packet """

chassis_id = pkt.chassis_id(subtype=pkt.chassis_id.SUB_LOCAL)
chassis_id.id = bytes('dpid:' + hex(long(dpid))[2:-1])
# Maybe this should be a MAC.  But a MAC of what?  Local port, maybe?

port_id = pkt.port_id(subtype=pkt.port_id.SUB_PORT, id=str(port_num))

ttl = pkt.ttl(ttl = ttl)

sysdesc = pkt.system_description()
sysdesc.payload = bytes('dpid:' + hex(long(dpid))[2:-1])

discovery_packet = pkt.lldp()
discovery_packet.tlvs.append(chassis_id)
discovery_packet.tlvs.append(port_id)
discovery_packet.tlvs.append(ttl)
discovery_packet.tlvs.append(sysdesc)
discovery_packet.tlvs.append(pkt.end_tlv())

eth = pkt.ethernet(type=pkt.ethernet.LLDP_TYPE)
eth.src = port_addr
eth.dst = pkt.ETHERNET.NDP_MULTICAST
eth.payload = discovery_packet

return eth

`

MurphyMc commented 4 years ago

Just create an instance of organizationally_specific. Then set the .oui and possibly the .subtype to something that identifies you (e.g., your organization's OUI) and your custom type. Then set the .payload to a random UUID and timestamp however you want to do that. For example, you could make it human-readable and just make payload a string containing "{uuid...} timestamp". I think that's all there is to it.

If I recall correctly, getting discovery to send additional TLVs should be as simple as appending them to the list with the other TLVs: https://github.com/MurphyMc/pox/blob/fangtooth/pox/openflow/discovery.py#L194

However, I think the current version pre-generates the LLDP messages and recycles them. If you want a fresh timestamp, you'll need to generate them on the fly instead. The current pre-generation is done in add_port(); it calls create_packet_out() to do the actual packet generation, wraps the resulting packet in a SendItem() and then adds it to the list of packets to send each cycle. I think the easiest fix is to have the SendItem() contain the tuple (dpid, port_num, port_addr) instead of the packet. Then when sending (in _timer_handler()), use those as parameters to create_packet_out() then.

Then you'll want to modify the PacketIn handler: https://github.com/MurphyMc/pox/blob/fangtooth/pox/openflow/discovery.py#L342

I think you'll want to iterate through the list of TLVs looking for ones of tlv_type==ORGANIZATIONALLY_SPECIFIC_TLV. When you find one, check if it's got your OUI and subtype. If so, parse its .payload.

joekutos commented 4 years ago

SHould it be done in the Discovery.py or the lldp.py here are my variables: type = 127 since it is a custom tlv oui = 'keilo' subtype = 1 random = {'UUID':'UHHXXX888323', 'TIMESTAMP':'time'} This is what i am looking at adding two files or should they both be separate ? can this be done in the discovery.py class instantiation or in the lldp.py.

joekutos commented 4 years ago

Hello Murphy, I am new to pox but know about SDN and currently carrying out research in SDN could get me a simple code snippet to find a solution

On Tue, Oct 22, 2019 at 10:29 AM Murphy notifications@github.com wrote:

Just create an instance of organizationally_specific. Then set the .oui and possibly the .subtype to something that identifies you (e.g., your organization's OUI) and your custom type. Then set the .payload to a random UUID and timestamp however you want to do that. For example, you could make it human-readable and just make payload a string containing "{uuid...} timestamp". I think that's all there is to it.

If I recall correctly, getting discovery to send additional TLVs should be as simple as appending them to the list with the other TLVs: https://github.com/MurphyMc/pox/blob/fangtooth/pox/openflow/discovery.py#L194

However, I think the current version pre-generates the LLDP messages and recycles them. If you want a fresh timestamp, you'll need to generate them on the fly instead. The current pre-generation is done in add_port(); it calls create_packet_out() to do the actual packet generation, wraps the resulting packet in a SendItem() and then adds it to the list of packets to send each cycle. I think the easiest fix is to have the SendItem() contain the tuple (dpid, port_num, port_addr) instead of the packet. Then when sending (in _timer_handler()), use those as parameters to create_packet_out() then.

Then you'll want to modify the PacketIn handler:

https://github.com/MurphyMc/pox/blob/fangtooth/pox/openflow/discovery.py#L342

I think you'll want to iterate through the list of TLVs looking for ones of tlv_type==ORGANIZATIONALLY_SPECIFIC_TLV. When you find one, check if it's got your OUI and subtype. If so, parse its .payload.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/noxrepo/pox/issues/230?email_source=notifications&email_token=ABYIRWTSY5OKCYDFS2PLTQ3QP2TW5A5CNFSM4JC4UFG2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEB4ZKIY#issuecomment-544838947, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABYIRWVXDQOR6QIUMSL4D7DQP2TW5ANCNFSM4JC4UFGQ .

-- Joseph Katongole UNIX Systems Engineer +256392944768

MurphyMc commented 4 years ago

It's probably hard to alter discovery's behavior this way via subclassing, so certainly the easiest thing to do is to just hack up discovery.py.

'keilo' isn't a valid OUI. It is a 24 bit number (encoded as a string/bytes) which is acquired from IEEE. See the wikipedia article.

In discovery.py around line 194 (https://github.com/MurphyMc/pox/blob/fangtooth/pox/openflow/discovery.py#L194), you'd do something like...

    # new stuff
    mytlv = pkt.organizationally_specific()
    mytlv.sybtype = 1 # whatever you want
    mytlv.oui = b"\x00\xca\xfe" # replace with real OUI
    mytlv.payload = "ccc4f430-9855-4f87-8fd9-85c4ca7b18de 1571769186"

    discovery_packet = pkt.lldp()
    discovery_packet.tlvs.append(chassis_id)
    discovery_packet.tlvs.append(port_id)
    discovery_packet.tlvs.append(ttl)
    discovery_packet.tlvs.append(sysdesc)
    discovery_packet.tlvs.append(mytlv) # this line is new
    discovery_packet.tlvs.append(pkt.end_tlv())

Then in _handle_openflow_PacketIn() probably right before the end (https://github.com/MurphyMc/pox/blob/fangtooth/pox/openflow/discovery.py#L467), you'd want to look for your custom TLV and do something with it...

    # look for and handle custom TLV...
    for tlv in lldph.tlvs:
      if tlv.tlv_type != pkt.lldp.ORGANIZATIONALLY_SPECIFIC_TLV: continue
      if tlv.oui != b"\x00\xca\xfe": continue # replace with real OUI
      if tlv.subtype != 1: continue # or whatever your subtype is
      # This should be our custom TLV...
      uuid,timestamp = tlv.payload.split()
      ... do something with UUID and timestamp ...

    return EventHalt # Probably nobody else needs this event

I haven't tested any of that at all, but hopefully it will get you started. The timestamps will only be generated once, so if you want new ones with every packet, refer to my post above where I suggest a modification to have it generate new ones. (But I'd do that after you get the simple case working first.)

joekutos commented 4 years ago

This good atleast i can get the added tlv in the packet in message. this has to be placed before the link is created. The checks done need to match with those that were in the packet out message. link = Discovery.Link(originatorDPID, originatorPort, event.dpid, event.port)

if link not in self.adjacency:
  self.adjacency[link] = time.time()
  log.info('link detected: %s', link)
  self.raiseEventNoErrors(LinkEvent, True, link, event)
else:
  # Just update timestamp
  self.adjacency[link] = time.time()
`

Because certain actions have to follow. before the link is created let me make some modifications i will let you know how it goes

`

joekutos commented 4 years ago

I did add the code to the discovery.py file just before a link is created the validation of the custom tlv should be done. so i decided to add this information right before the link event is called to detect a link. ` for tlv in lldph.tlvs[4:]:

    if tlv.tlv_type == pkt.lldp.ORGANIZATIONALLY_SPECIFIC_TLV:
        if tlv.oui == b'\xf8\x7E\x9A' and tlv.subtype == 1:

            #continue
        uuid,timestamp = tlv.payload.split()
            #print uuid

link = Discovery.Link(originatorDPID, originatorPort, event.dpid,
                      event.port)

` its like the uuid and timestamp are not within scope

joekutos commented 4 years ago

Hello @Murphy its just not working as expected maybe we could go through the code together. My email is joekutos@gmail.com would like to contact you so we can discuss more about it

joekutos commented 4 years ago

This piece of code finally worked and the packet information if first verified before the link is detected and created. Maybe now this should be dynamically created but it is working as expected ` if lldph.tlvs[4].tlv_type != pkt.lldp.ORGANIZATIONALLY_SPECIFIC_TLV: log.warning("Packet is not as expeced") return EventHalt

if lldph.tlvs[4].oui != b'\xf8\x7E\x9F' and lldph.tlvs[4].subtype != 1:
  log.warning("OUI and Subtype donot match")
  return EventHalt

uuid,timestamp = lldph.tlvs[4].payload.split()
#if lldph.tlvs[4].payload
print(uuid + ',' + timestamp)

`

joekutos commented 4 years ago

only problem is ensuring the oui really matches checked through the lldp.py and the oui and subtype are transmitted as binary struct format together. from lldp.py def _pack_data (self): return struct.pack('!3sB', self.oui, self.subtype) + self.payload The if statement is always matching ie the oui from the print in the packet in handler is returning ?~? so it is macthing for any oui is use just need to make sure that the value of the oui if a different one is provided the link should not be created which is not the case here.

`INFO:openflow.of_01:[00-00-00-00-00-02 3] connected DEBUG:forwarding.l2_learning:Connection [00-00-00-00-00-02 3] ?~? INFO:openflow.discovery:link detected: 00-00-00-00-00-01.1 -> 00-00-00-00-00-03.1 ?~? INFO:openflow.discovery:link detected: 00-00-00-00-00-01.2 -> 00-00-00-00-00-02.1 ?~? INFO:openflow.discovery:link detected: 00-00-00-00-00-03.1 -> 00-00-00-00-00-01.1 ?~? INFO:openflow.discovery:link detected: 00-00-00-00-00-02.1 -> 00-00-00-00-00-01.2 ?~? ?~?

`

the if statement is matching for any value of the oui if lldph.tlvs[4].oui != b'\xf8\x7E\x9F' and lldph.tlvs[4].subtype != 1: log.warning("OUI and Subtype donot match") return EventHalt

MurphyMc commented 4 years ago
if lldph.tlvs[4].oui != b'\xf8\x7E\x9F' and lldph.tlvs[4].subtype != 1:

Shouldn't that be an OR and not an AND?

joekutos commented 4 years ago

I think it should be an AND because both conditions must be satisfied for the data to be valid

MurphyMc commented 4 years ago

Exactly. But the line is using != instead of ==, so it's actually checking for the cases which are not valid. And it's not valid if either case is not valid, thus OR instead of AND. (One way to think of this is that it's an application of De Morgan's law.)

joekutos commented 4 years ago

@MurphyMc what are some of the characteristics that can be added to an LLDP packet to make it unique enough that we can assume it has been crafted i would like to build a model that i can adopt and use deep learning to be able to decided whether or not a link has been fabricated. Secondly how can we generate crafted lldp packets from the scapy library

MurphyMc commented 4 years ago

The idea being that you want to be able to tell if an attacker is trying to convince the controller that the topology is different than it actually is?

It's relatively easy to make sure that a packet is one that the controller itself generated. Put a nonce in it and have the controller verify it, or digitally sign it, for example.

But there's nothing that stops an attacker with good positioning from taking an LLDP packet from one place and playing it back somewhere else except like... latency measurements, maybe. You need to actually secure your topology somehow for this. One way would be filtering such that hosts can't send/read LLDP, but that assumes you at least know where hosts attach. In my opinion, this is reasonable, but I don't really believe in ongoing automatic topology discovery for most scenarios to begin with.

joekutos commented 4 years ago

Exactly thats the idea that an attacker should not convince the controller that the topology is different. This is the idea. To disable hosts from sending packets that means i should have a host tracking service that can tell if a port has a host connected to it. Secondly that would mean adding some modification to the payload and before a link is created an algorithmn should first be trained in a way to be able to know if a link has been fabricated or not. I was thinking of adding some encrypted data that would probably be a combination of the IP address and mac address of the controller plus some other information to be able to train a model that can discover whether or not a link has been fabricated. Supervised learning model is what i am looking at. can we discuss this off git hub for now?

MurphyMc commented 4 years ago

I'm pretty skeptical of any approach that uses automatic topology discovery. This probably makes me a bad person to ask, since my first answer is generally going to be "Don't do that." It really helps to have ground truth which discovery can't provide. For example, any host tracking service is susceptible to attack too. If you can fake not being a host, then you can fake being a link. My typical answer is that administrator configuration and physical security provides this, and that in the absence of administrator configuration and physical security, you can't really expect much.

But my email address is in all of my commits.

joekutos commented 4 years ago

Here is my challenge trying to administratively manage the link detection as we had earlier discussed but i realised i need to have some external script to be able to add and delete links from the flow table. After the packet-in message has been received at what point does the controller decide to let traffic through that link detected link. Plus i need to intercept the link creation so i can collect the links and be able to choose which links to add and which links not to add. Is this a good approach?

On Mon, Nov 4, 2019 at 5:54 PM Murphy notifications@github.com wrote:

I'm pretty skeptical of any approach that uses automatic topology discovery. This probably makes me a bad person to ask, since my first answer is generally going to be "Don't do that." It really helps to have ground truth which discovery can't provide. For example, any host tracking service is susceptible to attack too. If you can fake not being a host, then you can fake being a link. My typical answer is that administrator configuration and physical security provides this, and that in the absence of administrator configuration and physical security, you can't really expect much.

But my email address is in all of my commits.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/noxrepo/pox/issues/230?email_source=notifications&email_token=ABYIRWWAK2ZOVWOEH47V7ODQSAZT5A5CNFSM4JC4UFG2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEC7QVAI#issuecomment-549390977, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABYIRWTQTRAK7JQOJTYXPXTQSAZT5ANCNFSM4JC4UFGQ .

-- Joseph Katongole UNIX Systems Engineer +256392944768

MurphyMc commented 4 years ago

None of this sounds like it needs to be external. It sounds like you could write it as a POX component. If you did want to write it to be external, POX has various things that may help with this or at least could be starting points. For example, the messenger service, the OpenFlow webservice component, etc.

As for what point after a packet-in message has been received does the controller let traffic through... this is really up to you. In l2_learning, for example, it happens immediately. For the proactive ones, it doesn't get a packet-in at all.

joekutos commented 4 years ago

@MurphyMc please break it down for me clearly. i am thinking as we had discussed of administratively managing these links. ie does that mean we are dealing away with LLDP packets ? what i meant by externally is that how can i interact with an actively running controller to be able to add and drop links that have been created. And what happens when a new link is created?

joekutos commented 4 years ago

Hello @MurphyMc what do you think about the previous post?

joekutos commented 4 years ago

does the manual/administrative procedure eliminate the need for LLDP packets

MurphyMc commented 4 years ago

It's up to you. If you actually know the topology ahead of time, there's no need. If you want to discover the topology then you still need LLDP (or the equivalent), and the difference is just that you have to approve a link before using it.

joekutos commented 4 years ago

oh i get it. lets take a scenario where i have to discover the topology. My question was at what point do i get to approve this link? how interactively would this approval be. ie i am talking about the discovery.py where the topology discovery happens. how would intercept to approve the links? thats i think that part i needed to look deeper. think mostly the part holding me back a bit

joekutos commented 4 years ago

looking at how to actually interact with an already running controller

MurphyMc commented 4 years ago

I think the cleanest thing would be to modify/build the forwarding application around the idea. For example, take topo_proactive.

That part of the code is triggered by a discovery link event and updates topo_proactive's data structures and computes paths. You could just rewrite this so that instead of being triggered directly by a discovery event, it's triggered by an approval event, managed by some approval component (which itself probably consumes discovery events).

joekutos commented 4 years ago

okay sure does pox have some api i can work with to be able to interact with the controller? i am looking at how to externally send the approval event

MurphyMc commented 4 years ago

Many.

First off, it's just Python. You could integrate whatever you wanted. Like RPyC, for example.

POX also has its web module, based on Python's web stuff, so it's got built in support for writing web interfaces.

POX has a JSON-RPC module built using the above which can easily be used to expose functionality by JSON-RPC. The OpenFlow webservice is an example.

Then there's the messenger, which is a flexible system for building external interfaces to POX. Among other things, this has been used as the underlying technology behind POXDesk.

The messenger can use multiple different "transports". One thing which should be a transport but hasn't been integrated yet is POX's relatively recent support for websockets. But one could use that "raw" instead of as part of messenger.

joekutos commented 4 years ago

Hello Murphy trying to look at this kind of process flow

Process Flow Scenarios

New Links During the first link detection:

New links with old links

What do you think about this process flow. What how would i store these already created links

joekutos commented 4 years ago

Hello @MurphyMc secondly in this case what events am i going to look out for in this case. here is the scenario as earlier discussed. at the beginning of discovery, first time the links are detected, intervention happens to accept the links. This is probably a link event secondly. earliler we had also talked about modification of the lldp packet, this is probably handling the packet out even. Please let me know what you think and how we can go about it