Kurt Kanzenbach
2021-Nov-16 10:19 UTC
[Bridge] RFC: PTP Boundary Clock over UDPv4/UDPv6 on Linux bridge
Hi all, I'm currently trying to setup a PTP Boundary Clock over UDPv4 or UDPv6 on top of a switch using a Linux bridge. It works fine using PTP Layer 2 transport, but not for UDP. I'm wondering whether this is supported using Linux or if I'm doing something wrong. My setup looks like this: Bridge (DSA): |$ ip link set eth0 up |$ ip link set lan0 up |$ ip link set lan1 up |$ ip link add name br0 type bridge |$ ip link set dev lan0 master br0 |$ ip link set dev lan1 master br0 |$ ip link set br0 up |$ dhclient br0 PTP: |$ ptp4l -4 -i lan0 -i lan1 --tx_timestamp_timeout=40 -m It seems like ptp4l cannot receive any PTP messages. Tx works fine. The following hack solves the problem for me. However, I'm not sure whether that's the correct approach or not. Any opinions, ideas, comments? Thanks, Kurt |From 2e8b429b3ebabda8e81693b9704dbe5e5205ab09 Mon Sep 17 00:00:00 2001 |From: Kurt Kanzenbach <kurt at linutronix.de> |Date: Wed, 4 Aug 2021 09:33:12 +0200 |Subject: [PATCH] net: bridge: input: Handle PTP over UDPv4 and UDPv6 | |PTP is considered management traffic. A time aware switch should intercept all |PTP messages and handle them accordingly. The corresponding Linux setup is like |this: | | +-- br0 --+ | / / | \ | / / | \ | / | | / \ | / | | / \ | swp0 swp1 swp2 swp3 swp4 | |ptp4l runs on all individual switch ports and needs full control over sending |and receiving messages on these ports. | |However, the bridge code treats PTP messages over UDP transport as regular IP |messages and forwards them to br0. This way, the running ptp4l instances cannot |receive these frames on the individual switch port interfaces. | |Fix it by intercepting PTP UDP traffic in the bridge code and pass them to the |regular network processing. | |Signed-off-by: Kurt Kanzenbach <kurt at linutronix.de> |--- | net/bridge/br_input.c | 13 +++++++++++++ | 1 file changed, 13 insertions(+) | |diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c |index b50382f957c1..4e12be70a003 100644 |--- a/net/bridge/br_input.c |+++ b/net/bridge/br_input.c |@@ -271,6 +271,13 @@ static int br_process_frame_type(struct net_bridge_port *p, | return 0; | } | |+static const unsigned char ptp_ip_destinations[][ETH_ALEN] = { |+ { 0x01, 0x00, 0x5e, 0x00, 0x01, 0x81 }, /* IPv4 PTP */ |+ { 0x01, 0x00, 0x5e, 0x00, 0x00, 0x6b }, /* IPv4 P2P */ |+ { 0x33, 0x33, 0x00, 0x00, 0x01, 0x81 }, /* IPv6 PTP */ |+ { 0x33, 0x33, 0x00, 0x00, 0x00, 0x6b }, /* IPv6 P2P */ |+}; |+ | /* | * Return NULL if skb is handled | * note: already called with rcu_read_lock |@@ -280,6 +287,7 @@ static rx_handler_result_t br_handle_frame(struct sk_buff **pskb) | struct net_bridge_port *p; | struct sk_buff *skb = *pskb; | const unsigned char *dest = eth_hdr(skb)->h_dest; |+ int i; | | if (unlikely(skb->pkt_type == PACKET_LOOPBACK)) | return RX_HANDLER_PASS; |@@ -360,6 +368,11 @@ static rx_handler_result_t br_handle_frame(struct sk_buff **pskb) | if (unlikely(br_process_frame_type(p, skb))) | return RX_HANDLER_PASS; | |+ /* Check for PTP over UDPv4 or UDPv6. */ |+ for (i = 0; i < ARRAY_SIZE(ptp_ip_destinations); ++i) |+ if (ether_addr_equal(ptp_ip_destinations[i], dest)) |+ return RX_HANDLER_PASS; |+ | forward: | switch (p->state) { | case BR_STATE_FORWARDING: |-- |2.30.2 -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 861 bytes Desc: not available URL: <http://lists.linuxfoundation.org/pipermail/bridge/attachments/20211116/9a431e16/attachment.sig>
Vladimir Oltean
2021-Nov-16 10:21 UTC
[Bridge] RFC: PTP Boundary Clock over UDPv4/UDPv6 on Linux bridge
On Tue, Nov 16, 2021 at 11:19:24AM +0100, Kurt Kanzenbach wrote:> Hi all, > > I'm currently trying to setup a PTP Boundary Clock over UDPv4 or UDPv6 > on top of a switch using a Linux bridge. It works fine using PTP Layer 2 > transport, but not for UDP. I'm wondering whether this is supported > using Linux or if I'm doing something wrong. > > My setup looks like this: > > Bridge (DSA): > > |$ ip link set eth0 up > |$ ip link set lan0 up > |$ ip link set lan1 up > |$ ip link add name br0 type bridge > |$ ip link set dev lan0 master br0 > |$ ip link set dev lan1 master br0 > |$ ip link set br0 up > |$ dhclient br0 > > PTP: > > |$ ptp4l -4 -i lan0 -i lan1 --tx_timestamp_timeout=40 -m > > It seems like ptp4l cannot receive any PTP messages. Tx works fine. > > The following hack solves the problem for me. However, I'm not sure > whether that's the correct approach or not. Any opinions, ideas, > comments? > > Thanks, > Kurt > > |From 2e8b429b3ebabda8e81693b9704dbe5e5205ab09 Mon Sep 17 00:00:00 2001 > |From: Kurt Kanzenbach <kurt at linutronix.de> > |Date: Wed, 4 Aug 2021 09:33:12 +0200 > |Subject: [PATCH] net: bridge: input: Handle PTP over UDPv4 and UDPv6 > | > |PTP is considered management traffic. A time aware switch should intercept all > |PTP messages and handle them accordingly. The corresponding Linux setup is like > |this: > | > | +-- br0 --+ > | / / | \ > | / / | \ > | / | | / \ > | / | | / \ > | swp0 swp1 swp2 swp3 swp4 > | > |ptp4l runs on all individual switch ports and needs full control over sending > |and receiving messages on these ports. > | > |However, the bridge code treats PTP messages over UDP transport as regular IP > |messages and forwards them to br0. This way, the running ptp4l instances cannot > |receive these frames on the individual switch port interfaces. > | > |Fix it by intercepting PTP UDP traffic in the bridge code and pass them to the > |regular network processing. > | > |Signed-off-by: Kurt Kanzenbach <kurt at linutronix.de> > |--- > | net/bridge/br_input.c | 13 +++++++++++++ > | 1 file changed, 13 insertions(+) > | > |diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c > |index b50382f957c1..4e12be70a003 100644 > |--- a/net/bridge/br_input.c > |+++ b/net/bridge/br_input.c > |@@ -271,6 +271,13 @@ static int br_process_frame_type(struct net_bridge_port *p, > | return 0; > | } > | > |+static const unsigned char ptp_ip_destinations[][ETH_ALEN] = { > |+ { 0x01, 0x00, 0x5e, 0x00, 0x01, 0x81 }, /* IPv4 PTP */ > |+ { 0x01, 0x00, 0x5e, 0x00, 0x00, 0x6b }, /* IPv4 P2P */ > |+ { 0x33, 0x33, 0x00, 0x00, 0x01, 0x81 }, /* IPv6 PTP */ > |+ { 0x33, 0x33, 0x00, 0x00, 0x00, 0x6b }, /* IPv6 P2P */ > |+}; > |+ > | /* > | * Return NULL if skb is handled > | * note: already called with rcu_read_lock > |@@ -280,6 +287,7 @@ static rx_handler_result_t br_handle_frame(struct sk_buff **pskb) > | struct net_bridge_port *p; > | struct sk_buff *skb = *pskb; > | const unsigned char *dest = eth_hdr(skb)->h_dest; > |+ int i; > | > | if (unlikely(skb->pkt_type == PACKET_LOOPBACK)) > | return RX_HANDLER_PASS; > |@@ -360,6 +368,11 @@ static rx_handler_result_t br_handle_frame(struct sk_buff **pskb) > | if (unlikely(br_process_frame_type(p, skb))) > | return RX_HANDLER_PASS; > | > |+ /* Check for PTP over UDPv4 or UDPv6. */ > |+ for (i = 0; i < ARRAY_SIZE(ptp_ip_destinations); ++i) > |+ if (ether_addr_equal(ptp_ip_destinations[i], dest)) > |+ return RX_HANDLER_PASS; > |+ > | forward: > | switch (p->state) { > | case BR_STATE_FORWARDING: > |-- > |2.30.2 >This should do the trick as well? /sbin/ebtables --table broute --append BROUTING --protocol 0x88F7 --jump DROP /sbin/ebtables --table broute --append BROUTING --protocol 0x0800 --ip-protocol udp --ip-destination-port 320 --jump DROP /sbin/ebtables --table broute --append BROUTING --protocol 0x0800 --ip-protocol udp --ip-destination-port 319 --jump DROP /sbin/ebtables --table broute --append BROUTING --protocol 0x86DD --ip6-protocol udp --ip6-destination-port 320 --jump DROP /sbin/ebtables --table broute --append BROUTING --protocol 0x86DD --ip6-protocol udp --ip6-destination-port 319 --jump DROP