DEV Community

Cover image for UDP as a Serverless Event Source

UDP as a Serverless Event Source

Are you versed in Event Driven Architecture (EDA)? This article is going to explore applying EDA at the outside edge of systems as a boundary for traditional network traffic.

What is EDA?

Setting aside hyperbole and focusing on the "what":

Event-driven architectures have three key components: event producers, event routers, and event consumers. [AWS Documentation]

But what is an "event"?

A record of a significant change in state or a notable occurrence within a system, stated in the past tense. [Wikipedia]

In EDA, things happen and information about those occurrances flows from producers to consumers. Consumers may, of course, produce events of their own creating "flows" of events around and between systems. That sounds a lot like what network equipment does, right?

So let's make that literal.

Modeling a UDP "Event"

The User Datagram Protocol is simple intentionally. It was designed in the 1980's as the "catch-all" mechanism for moving data around the internet when TCP wasn't appropriate.

A UDP packet is just a bunch of bytes following a well-defined format:

[UDP Header][UDP Payload]
Enter fullscreen mode Exit fullscreen mode

Okay, it's not quite that simple because UDP is an extension of another protocol called the Internet Protocol (IP). And IP is almost always carried over ethernet, so the full content of a unit of data travelling the internet as UDP is really:

[Ethernet Header][IP Header][UDP Header][UDP Payload]
Enter fullscreen mode Exit fullscreen mode

Okay, okay, it's not that simple either but I'm going to leave it there for this article. In fact, I won't be talking about the ethernet header beyond this point so we can just focus on the UDP part (and a little about IP later).

Breaking out the UDP header in more detail:

[Source Port][Destination Port][Length][Checksum][Payload]
Enter fullscreen mode Exit fullscreen mode

Normally the operating system parses that header to decide which application or service should receive it. A UDP packet with a destination port of 514 is often delivered to rsyslogd, for example. If we're going to treat that UDP packet as an event inside AWS we're going to need a JSON model of it, like:

{
  "Destination: { "Port": 514 },
  "Payload": "<base64 encoded payload?"
}
Enter fullscreen mode Exit fullscreen mode

Pretty minimal, but you get the idea. What happened to SourcePort and Checksum you ask? We're just ignoring them. In reality they're optional and you may get 0 values for both in some (many?) situations.

Routing and Delivery

With that model we can listen for UDP packets and fire an event to EventBridge for each:

import socket, struct, base64, json, boto3

events = boto3.client("events")
sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_UDP)

while True:
    pkt, _ = sock.recvfrom(65535)

    ihl = (pkt[0] & 0x0F) * 4
    dst_port = struct.unpack("!H", pkt[ihl+2:ihl+4])[0]
    payload = pkt[ihl+8:]

    events.put_events(Entries=[{
        "Source": "udp.raw",
        "DetailType": "UdpPacket",
        "Detail": json.dumps({
            "Destination": {"Port": dst_port},
            "Payload": base64.b64encode(payload).decode()
        })
    }])
Enter fullscreen mode Exit fullscreen mode

And from there we can use Rules to direct the packets to consumers like Lambda, Step Function, DynamoDB or S3. The possibilities are endless.

Let's take an example. We could setup a Rule that filters everything destined for port 514:

aws events put-rule \
  --name udp-port-514 \
  --event-pattern '{
    "source": ["udp.raw"],
    "detail": {
      "Destination": {
        "Port": [514]
      }
    }
  }'
Enter fullscreen mode Exit fullscreen mode

Now we can direct those packets to a Firehose for archival:

aws events put-targets \
  --rule udp-port-514 \
  --targets "Id"="firehose-target","Arn"="arn:aws:firehose:REGION:ACCOUNT_ID:deliverystream/YOUR_STREAM"
Enter fullscreen mode Exit fullscreen mode

Or to CloudWatch Logs, or maybe both:

aws events put-targets \
  --rule udp-port-514 \
  --targets "Id"="cwlogs-target","Arn"="arn:aws:logs:REGION:ACCOUNT_ID:log-group:/eventbridge/udp-514"
Enter fullscreen mode Exit fullscreen mode

That's the flexibility of EDA applied to network traffic. Here the use case is as simple as it gets, but that's just to pique your interest. Far more complicated scenarios benefit even more from EDA due to strong decoupling and the scalability of AWS services.

Caveats

Scalability can work against you.

Network traffic can be FAST. Really, really fast. Connecting a public-facing service directly to AWS resources like this is a terrible idea. It can lead to huge costs should someone decide (on purpose or accidentally) to DDoS you.

Strong, stateful firewalls with rate limiting and abuse prevention must be in place.

Summary

EDA is a powerful architecture pattern that applies to networking events as well as application generated events. Thinking more broadly about the "edge" of your systems opens-up great potential for simplification and, honestly, is just plain fun.

Top comments (0)