{"id":410956,"date":"2024-06-29T21:41:22","date_gmt":"2024-06-29T21:41:22","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=410956"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=410956","title":{"rendered":"<span>Linux Switchdev the Mellanox way<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-1\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<blockquote><p>This is a transcription of a talk that was presented <a href=\"https:\/\/indico.csnog.eu\/event\/7\/contributions\/85\/\">at CSNOG 2020<\/a> \u2014 video is at the end of the page<\/p><\/blockquote>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/wc\/cj\/tt\/wccjttxkfffspxaxg2_4idt3c_8.png\" data-src=\"https:\/\/habrastorage.org\/webt\/wc\/cj\/tt\/wccjttxkfffspxaxg2_4idt3c_8.png\"\/><\/p>\n<p>  Greetings! My name is Alexander Zubkov. I work at Qrator Labs, where we protect our customers against DDoS attacks and provide BGP analytics.<\/p>\n<p>  We started using Mellanox switches around 2 or 3 years ago. At the time we got acquainted with Switchdev in Linux and today I want to share with you our experience.<br \/>  <a name=\"habracut\"><\/a><br \/>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/bb\/s_\/wn\/bbs_wneiwvlwlrg1-vujgwcdz0y.png\" data-src=\"https:\/\/habrastorage.org\/webt\/bb\/s_\/wn\/bbs_wneiwvlwlrg1-vujgwcdz0y.png\"\/><\/p>\n<p>  We use Mellanox switches on the Spectrum chipset \u2014 the so-called \u201cwhite-box\u201d switches. It means that you could install various operating systems on it, whichever you want, from different vendors. For example, among those you can see Mellanox\u2019s own operating system \u2014 called Onyx. Cumulus also supports the switches. Besides, you have an SDK to create your own environment. Also, there is support for SAI (Switch Abstraction Interface) SDK \u2014 as I understand it is an effort to make a standard SDK for switches, which is used by SONiC operating system. And of course, they support switchdev \u2014 and that means you can install whatever Linux distribution you like on the Mellanox switch.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/i4\/ks\/fo\/i4ksfozaougaklpt__kecowzvdo.png\" data-src=\"https:\/\/habrastorage.org\/webt\/i4\/ks\/fo\/i4ksfozaougaklpt__kecowzvdo.png\"\/><\/p>\n<p>  So, what is Switchdev? Switchdev is an in-kernel infrastructure that allows the driver to offload the network configuration from the Linux kernel to the data plane of the switch. So you work with the switch the same way you work with a server with many interfaces. Furthermore, you work with standard Linux primitives like interfaces, routing tables, and so on. Switchdev here makes and provides some generic means to drivers.<\/p>\n<p>  Mellanox switches widely support the Switchdev. And I know that some of Broadcom switches also support Switchdev, as well as some other network equipment.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/db\/ts\/dt\/dbtsdtvnipdsfokmmoqgchponki.png\" data-src=\"https:\/\/habrastorage.org\/webt\/db\/ts\/dt\/dbtsdtvnipdsfokmmoqgchponki.png\"\/><\/p>\n<p>  We use the mentioned features in Mellanox switches, which are all supported in hardware and provided into Switchdev. Those are the most common features among switches, except for tunnels, which I think are not that widely popular. However, we like this feature very much.<\/p>\n<p>  Some features are missing that we would like to see on that list, like the policy-based routing.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/kg\/sy\/zn\/kgsyznmw0jaro-wrwntne1ieaku.png\" data-src=\"https:\/\/habrastorage.org\/webt\/kg\/sy\/zn\/kgsyznmw0jaro-wrwntne1ieaku.png\"\/><\/p>\n<p>  The Mellanox driver which supports Switchdev is included in the main \u201cvanilla\u201d kernel version, so you can use even the kernel version packaged with your distribution if it contains all the required options. And, if you need some bleeding-edge features, you can use the \u201cnet-next\u201d version, because the features first arrive there and, after standardization, are transferred to the vanilla kernel.<\/p>\n<p>  With the recent driver versions and recent firmwares, you do not need to take care of the switch\u2019s firmware. Because the driver loads the required firmware during the initialization phase, only if you have some old switch firmware, you need to update it once, and then the driver will take care of that.<\/p>\n<p>  Of course, the driver needs some firmware image, which is commonly provided by the distribution packages named like \u201clinux-firmware\u201d. If you compile the kernel from source, you need to double-check if your distribution provides the required firmware or whether you need to download it manually. This could happen if it is relatively fresh and recently published.<\/p>\n<p>  You need to take care if your driver goes to initramfs and if it does not find the firmware file \u2014 it fails the initialization phase, so you have to reload the driver after the boot finishes. We just masked it from initramfs and do not care about it.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/pg\/qy\/vl\/pgqyvlscw7bgrq-jmvthey8lhle.png\" data-src=\"https:\/\/habrastorage.org\/webt\/pg\/qy\/vl\/pgqyvlscw7bgrq-jmvthey8lhle.png\"\/><\/p>\n<p>  Here is an overview of tools we use to configure the Switchdev, consider this a cheat sheet. Of course, there is an iproute set of tools; also I want to note a bridge tool which is used to configure VLAN mapping to ports, and devlink tool which is used to configure features like port split, hardware pool sizes and control plane policy limits. There is also a teamd tool available, which is another option to configure bond interfaces in Linux, but we are quite happy with the ip link tool, so we do not use the former.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/sk\/ai\/nw\/skainwdpxmuuu9vewirqjznfveu.png\" data-src=\"https:\/\/habrastorage.org\/webt\/sk\/ai\/nw\/skainwdpxmuuu9vewirqjznfveu.png\"\/><\/p>\n<p>  Next tools, like the ethtool we employ to watch port speed, statistics and some other relevant info. There is also llpad which is, surprisingly, used not only for discovery but also to configure some quality of service parameters, and it is called Linux Data Center Bridging. And you can use sysctl to configure some parameters that affect dataplane too.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/04\/sw\/ss\/04swss-zt3ls7vpjsnwlbjv9fyi.png\" data-src=\"https:\/\/habrastorage.org\/webt\/04\/sw\/ss\/04swss-zt3ls7vpjsnwlbjv9fyi.png\"\/><\/p>\n<p>  We have virtual routing here but it maps to the Linux vrf subsystem \u2014 if you are not familiar with it we should note that it is not the same as the network namespace. You have all your interfaces in the same namespace here, and different routing tables in Linux are used for the routing. There is a special ip rule that manages it. To configure it you need to create some particular vrf type interface and attach your interfaces to it with the help of a master option. There is one interesting feature which is offloaded to the switch too \u2014 we can have routes between vrfs. For that, we can add an explicit device option to our route, which points to other vrfs.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/wh\/nj\/ji\/whnjjiq7j70d9ubo1-rnn6h7nic.png\" data-src=\"https:\/\/habrastorage.org\/webt\/wh\/nj\/ji\/whnjjiq7j70d9ubo1-rnn6h7nic.png\"\/><\/p>\n<p>  So in order to configure your switch you need to make your configuration from the bottom upwards, or from ports to IP addresses and routes. Mostly this is restricted by the structure of Linux configuration, but there are some other restrictions, I could name a few \u2014 for example, you cannot attach a port to the bond if the port is up, you need to set it down in advance. Moreover, if you want to attach a port to the bond and the bond is attached to the bridge now \u2014 you will not be able to do that, you need to detach the bond from the bridge before.<\/p>\n<p>  At the beginning we had a big initialization script for that \u2014 it is mostly okay, although when we want to introduce some changes, we do not want to reboot switch every time to implement something small.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/to\/lr\/7e\/tolr7e2upnc6kfeyl_k-mq7fbaw.png\" data-src=\"https:\/\/habrastorage.org\/webt\/to\/lr\/7e\/tolr7e2upnc6kfeyl_k-mq7fbaw.png\"\/><\/p>\n<p>  And those changes become a nightmare sometimes, because you need to keep all the structure of your interfaces and those restrictions in your mind, and it is increasingly difficult to do.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/iy\/cc\/fo\/iyccfosk_qe-51zpseoaa-2d5ls.png\" data-src=\"https:\/\/habrastorage.org\/webt\/iy\/cc\/fo\/iyccfosk_qe-51zpseoaa-2d5ls.png\"\/><\/p>\n<p>  For example, if you have some other switches with a vendor environment \u2014 it could take care of that. With Linux though it is like low-level work and could be compared to Assembly language. However, thanks to Larry we have Perl here, and it takes care of that. I wrote a couple of tools, and one of them is mlxrtr (router) \u2014 you can see an example of its config at the slide. It results in such commands:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/dm\/cv\/ad\/dmcvadhpd_e7avzgo_ikioy7t3y.png\" data-src=\"https:\/\/habrastorage.org\/webt\/dm\/cv\/ad\/dmcvadhpd_e7avzgo_ikioy7t3y.png\"\/><\/p>\n<p>  Here we show some basic initialization, creating interfaces;<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/4-\/8s\/ny\/4-8snyumo2gfhb6hp8fskcinzva.png\" data-src=\"https:\/\/habrastorage.org\/webt\/4-\/8s\/ny\/4-8snyumo2gfhb6hp8fskcinzva.png\"\/><\/p>\n<p>  Then it sets masters for those interfaces.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/vy\/js\/d1\/vyjsd15hgwmomc5bp8opttqfdim.png\" data-src=\"https:\/\/habrastorage.org\/webt\/vy\/js\/d1\/vyjsd15hgwmomc5bp8opttqfdim.png\"\/><\/p>\n<p>  Then it maps VLANs to ports, bringing interfaces up.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/tj\/11\/bh\/tj11bhvfpm1sggtl7muz3pc-uv4.png\" data-src=\"https:\/\/habrastorage.org\/webt\/tj\/11\/bh\/tj11bhvfpm1sggtl7muz3pc-uv4.png\"\/><\/p>\n<p>  Finally, it adds IP addresses and routes. You can see that there are quite a lot of commands, and we managed a script like that by hand, at first, but as you can see the newer configuration is much simpler and more comfortable to comprehend.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/tl\/zk\/ih\/tlzkih-8bx9icqmntakm9ql8ovo.png\" data-src=\"https:\/\/habrastorage.org\/webt\/tl\/zk\/ih\/tlzkih-8bx9icqmntakm9ql8ovo.png\"\/><\/p>\n<p>  And the best part is making changes. For example, if you want to move one port to another bond, then you should simply change a couple of lines in your configuration. With other vendors like Cisco, it is pretty much the same, and you will also need to change a couple of lines.<\/p>\n<p>  But you can see that it is not that simple when you do it by hand.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/vf\/sf\/x1\/vfsfx1a941trcxbt9ukdsrtwt4u.png\" data-src=\"https:\/\/habrastorage.org\/webt\/vf\/sf\/x1\/vfsfx1a941trcxbt9ukdsrtwt4u.png\"\/><\/p>\n<p>  Let us move to ACLs now. The hardware ACLs in Switchdev are managed by a tc filter subsystem. Of course, you can use iptables to protect your control plane, but they are not offloaded. So you need to use tc filters for that. Moreover, you need to keep in mind that tc filters not only layer 3 traffic, which is forwarded but also layer 2 traffic that is bridged. There may be cases where it affects your configuration.<\/p>\n<p>  And there is an exciting feature called shared ACL in newer tc versions and kernels. It is called blocks. For example, if you want to have some shared filter between several ports, you can use only one filter and make it shared between those ports, so you do not need to clone it. It is useful because in Mellanox, at least, you can only attach those filters to physical ports. So if you are working with VLANs for example, that spans different ports, you need to clone that configuration between them somehow.<\/p>\n<p>  Also in tc filters, you have a goto statement, and it is offloaded to Mellanox too, bringing you nonlinear filter logic here, which is great in my opinion.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/z0\/zq\/vt\/z0zqvte3tc64wkj5bifwdfctkrc.png\" data-src=\"https:\/\/habrastorage.org\/webt\/z0\/zq\/vt\/z0zqvte3tc64wkj5bifwdfctkrc.png\"\/><\/p>\n<p>  And the second tool we have is the mlxacl \u2014 it manages our filter configuration. It works in the following manner \u2014 in the main chain number 0, it compares defined VLAN numbers and jumps to the relevant chain, and then we have chains for every VLAN we defined with the rules.<\/p>\n<p>  And the config you see here results in such commands:<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/a1\/mk\/hq\/a1mkhqecsaxrvt-hhyzemjmnzps.png\" data-src=\"https:\/\/habrastorage.org\/webt\/a1\/mk\/hq\/a1mkhqecsaxrvt-hhyzemjmnzps.png\"\/><\/p>\n<p>  It is also a bit scary, compared to the original config. You can see here two chains filled with rules, and zero chain have the rule to jump to the chain for VLAN number 10.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/ns\/sg\/v6\/nssgv6lwtd8jq2akkx_-ihpckwq.png\" data-src=\"https:\/\/habrastorage.org\/webt\/ns\/sg\/v6\/nssgv6lwtd8jq2akkx_-ihpckwq.png\"\/><\/p>\n<p>  If you want to make some changes, this tool manages it in the following way. For example, I deleted one of the rules from the VLAN chain, and it creates a new chain for that VLAN and then rearranges the numbers in zero chain to use that new chain.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/bo\/ih\/gy\/boihgysya4i8z_bvn2fgtg_uwdi.png\" data-src=\"https:\/\/habrastorage.org\/webt\/bo\/ih\/gy\/boihgysya4i8z_bvn2fgtg_uwdi.png\"\/><\/p>\n<p>  And if you want to do it by hand \u2014 of course, that deletion would result in only one command, but before doing so, you need to watch the current configuration. For example, here you can see the output of tc filter show for that configuration.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/of\/gz\/qy\/ofgzqyhfcnjyzmotr7zfn28s0ca.png\" data-src=\"https:\/\/habrastorage.org\/webt\/of\/gz\/qy\/ofgzqyhfcnjyzmotr7zfn28s0ca.png\"\/><\/p>\n<p>  And here you have only two and a half rules. So I think it is very unmanageable and much more challenging than it was with the case of the interface. Actually, mlxacl was the first tool I wrote, because I need to survive somehow.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/ry\/rr\/rb\/ryrrrb_ndwwlxo7l4c7nd9tlvde.png\" data-src=\"https:\/\/habrastorage.org\/webt\/ry\/rr\/rb\/ryrrrb_ndwwlxo7l4c7nd9tlvde.png\"\/><\/p>\n<p>  As I said, there is no policy-based routing, but we have one relevant task. We have such a route pattern, where we take all the traffic from the uplink, for example, and throw it to the set of servers. For example, in Cisco, you set an ip policy here and set the next hop pointing to your servers. We do something similar with Arista switches, but in Linux, you could do it by ip rule, although it is not offloaded.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/hx\/9g\/1g\/hx9g1gu2z7ykjhstd3drzxc47ro.png\" data-src=\"https:\/\/habrastorage.org\/webt\/hx\/9g\/1g\/hx9g1gu2z7ykjhstd3drzxc47ro.png\"\/><\/p>\n<p>  But we have routes between vrfs here, so we can do some tricks with that. We split our vrf into two parts, one is an external vrf with an uplink interface, and the second one is internal with our servers. And in uplink vrf we set a default route pointing to our servers. However, we created a little problem here, because our interfaces are now in different vrfs and their direct networks are isolated from each other \u2014 and you have to live with that.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/gq\/nh\/ep\/gqnhepbo1hlo6nzyfbxvhje5ade.png\" data-src=\"https:\/\/habrastorage.org\/webt\/gq\/nh\/ep\/gqnhepbo1hlo6nzyfbxvhje5ade.png\"\/><\/p>\n<p>  Here I tried to draw that scheme. You can see two vrfs here \u2014 the bottom is internal vrf, it contains common routes: default route via uplink and local route. In the external vrf at the top, you can see that it has a default route pointing to servers via the second vrf.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/r8\/vp\/ar\/r8vparety9hvcbfaq4pikoppgxe.png\" data-src=\"https:\/\/habrastorage.org\/webt\/r8\/vp\/ar\/r8vparety9hvcbfaq4pikoppgxe.png\"\/><\/p>\n<p>  And those tools that I mentioned above are published on GitLab by our company. They are MIT licensed, which means that you can use it for free and of course without warranty and liability.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/ic\/2p\/rs\/ic2prs49eiwg0t7jiy1bntg0-wc.png\" data-src=\"https:\/\/habrastorage.org\/webt\/ic\/2p\/rs\/ic2prs49eiwg0t7jiy1bntg0-wc.png\"\/><\/p>\n<p>  Those two tools are written in Perl language (because I know Perl and it just works), they contain almost no dependencies except for a couple of Perl modules that, I believe, are included in the core language, and they use external tools mentioned here. Some of them are called from \/root\/bin\/ path \u2014 do not be surprised by that as we needed newer versions of those tools, so we put there compiled by hand versions.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/rh\/h0\/34\/rhh0340dj7jz_uugwuiokcbxegc.png\" data-src=\"https:\/\/habrastorage.org\/webt\/rh\/h0\/34\/rhh0340dj7jz_uugwuiokcbxegc.png\"\/><\/p>\n<p>  Last but not least. If some of you think that it is another way of escaping Vim editor \u2014 and you are right. If you use a serial console to manage your switch, this sequence will help you to reset your switch if it freezes and for example, you get kernel panic. I believe if you enter that combo switch says \u201cFatality\u201d but you won\u2019t hear that with a remote switch. And another couple of useful tips for you as well on this slide \u2014 in Linux you may send a sys request to reboot your kernel or some other things, but when you use the serial console, this sys request is sent with the break signal. In tools like minicom or screen, you can send the break signal with those sequences. And if you want to get into the bios of the switch during boot, you should press Ctrl+B.<\/p>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/jx\/ru\/_2\/jxru_2tfjjvm-hs-qvtata0upwo.png\" data-src=\"https:\/\/habrastorage.org\/webt\/jx\/ru\/_2\/jxru_2tfjjvm-hs-qvtata0upwo.png\"\/><\/p>\n<p>  That is all I wanted to share with you, thanks for reading and listening.<\/p>\n<div class=\"oembed\">\n<div class=\"tm-iframe_temp\" data-src=\"https:\/\/embedd.srv.habr.com\/iframe\/5fa947e7cb79a1e12d666412\" data-style=\"\" id=\"5fa947e7cb79a1e12d666412\" width=\"\"><\/div>\n<\/div>\n<p>  \u2192 <a href=\"https:\/\/habr.com\/en\/company\/qrator\/blog\/527158\/\">Article in Russian<\/a> language.<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/527140\/\"> https:\/\/habr.com\/ru\/articles\/527140\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-1\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<blockquote><p>This is a transcription of a talk that was presented <a href=\"https:\/\/indico.csnog.eu\/event\/7\/contributions\/85\/\">at CSNOG 2020<\/a> \u2014 video is at the end of the page<\/p><\/blockquote>\n<p>  <img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/wc\/cj\/tt\/wccjttxkfffspxaxg2_4idt3c_8.png\" data-src=\"https:\/\/habrastorage.org\/webt\/wc\/cj\/tt\/wccjttxkfffspxaxg2_4idt3c_8.png\"\/><\/p>\n<p>  Greetings! My name is Alexander Zubkov. I work at Qrator Labs, where we protect our customers against DDoS attacks and provide BGP analytics.<\/p>\n<p>  We started using Mellanox switches around 2 or 3 years ago. At the time we got acquainted with Switchdev in Linux and today I want to share with you our experience.  <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-410956","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/410956","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=410956"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/410956\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=410956"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=410956"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=410956"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}