mirror of
https://codeberg.org/cunicu/cunicu.git
synced 2025-10-09 10:40:26 +08:00
42 lines
23 KiB
HTML
42 lines
23 KiB
HTML
<!doctype html>
|
||
<html lang="en" dir="ltr" class="docs-wrapper docs-doc-page docs-version-current plugin-docs plugin-id-default docs-doc-id-development/proxying">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="generator" content="Docusaurus v2.1.0">
|
||
<title data-rh="true">Proxying | cunīcu</title><meta data-rh="true" name="viewport" content="width=device-width,initial-scale=1"><meta data-rh="true" name="twitter:card" content="summary_large_image"><meta data-rh="true" property="og:url" content="https://cunicu.li/docs/development/proxying"><meta data-rh="true" name="docusaurus_locale" content="en"><meta data-rh="true" name="docsearch:language" content="en"><meta data-rh="true" name="docusaurus_version" content="current"><meta data-rh="true" name="docusaurus_tag" content="docs-default-current"><meta data-rh="true" name="docsearch:version" content="current"><meta data-rh="true" name="docsearch:docusaurus_tag" content="docs-default-current"><meta data-rh="true" property="og:title" content="Proxying | cunīcu"><meta data-rh="true" name="description" content="cunīcu implements multiple ways of running an ICE agent alongside WireGuard on the same UDP ports."><meta data-rh="true" property="og:description" content="cunīcu implements multiple ways of running an ICE agent alongside WireGuard on the same UDP ports."><link data-rh="true" rel="icon" href="/img/favicon.png"><link data-rh="true" rel="canonical" href="https://cunicu.li/docs/development/proxying"><link data-rh="true" rel="alternate" href="https://cunicu.li/docs/development/proxying" hreflang="en"><link data-rh="true" rel="alternate" href="https://cunicu.li/docs/development/proxying" hreflang="x-default"><link rel="alternate" type="application/rss+xml" href="/blog/rss.xml" title="cunīcu RSS Feed">
|
||
<link rel="alternate" type="application/atom+xml" href="/blog/atom.xml" title="cunīcu Atom Feed">
|
||
|
||
|
||
|
||
|
||
<link rel="preconnect" href="https://matomo.0l.de/piwik.js">
|
||
<noscript>
|
||
var img = document.createElement('img');
|
||
img.src = "https://matomo.0l.de/piwik.php?idsite=5&rec=1&url=https://cunicu.li" + location.pathname;
|
||
img.style = "border:0";
|
||
img.alt = "tracker";
|
||
|
||
var s = document.getElementsByTagName('script')[0];
|
||
s.parentNode.insertBefore(img,s);
|
||
</noscript>
|
||
<script>window.dev=void 0,(!0===window.dev||"1"!==navigator.doNotTrack&&"1"!==window.doNotTrack)&&(window._paq=window._paq||[],window._paq.push(["setTrackerUrl","https://matomo.0l.de/piwik.php"]),window._paq.push(["setSiteId","5"]),window._paq.push(["enableHeartBeatTimer"]),window.start=new Date,function(){var e=document,t=e.createElement("script"),o=e.getElementsByTagName("script")[0];t.type="text/javascript",t.async=!0,t.defer=!0,t.src="https://matomo.0l.de/piwik.js",o.parentNode.insertBefore(t,o)}(),!0===window.dev&&(console.debug("[Matomo] Tracking initialized"),console.debug("[Matomo] matomoUrl: https://matomo.0l.de, siteId: 5")))</script>
|
||
|
||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.13.24/dist/katex.min.css" integrity="sha384-odtC+0UGzzFL/6PNoE8rX/SPcQDXBJ+uRepguP4QkPCm2LBxH3FA3y+fKSiJ+AmM" crossorigin="anonymous"><link rel="stylesheet" href="/assets/css/styles.31713120.css">
|
||
<link rel="preload" href="/assets/js/runtime~main.0d972120.js" as="script">
|
||
<link rel="preload" href="/assets/js/main.d3aebcf9.js" as="script">
|
||
</head>
|
||
<body class="navigation-with-keyboard" data-theme="light">
|
||
<script>!function(){function t(t){document.documentElement.setAttribute("data-theme",t)}var e=function(){var t=null;try{t=localStorage.getItem("theme")}catch(t){}return t}();t(null!==e?e:"light")}()</script><div id="__docusaurus">
|
||
<div role="region" aria-label="theme.common.skipToMainContent"><a href="#" class="skipToContent_fXgn">Skip to main content</a></div><nav class="navbar navbar--fixed-top"><div class="navbar__inner"><div class="navbar__items"><button aria-label="Navigation bar toggle" class="navbar__toggle clean-btn" type="button" tabindex="0"><svg width="30" height="30" viewBox="0 0 30 30" aria-hidden="true"><path stroke="currentColor" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2" d="M4 7h22M4 15h22M4 23h22"></path></svg></button><a class="navbar__brand" href="/"><div class="navbar__logo"><img src="/img/cunicu_icon.svg" alt="cunīcu logo" class="themedImage_ToTc themedImage--light_HNdA"><img src="/img/cunicu_icon.svg" alt="cunīcu logo" class="themedImage_ToTc themedImage--dark_i4oU"></div><b class="navbar__title text--truncate">cunīcu</b></a><a aria-current="page" class="navbar__item navbar__link navbar__link--active" href="/docs">Docs</a><a class="navbar__item navbar__link" href="/blog">Blog</a></div><div class="navbar__items navbar__items--right"><a href="https://github.com/stv0g/cunicu" target="_blank" rel="noopener noreferrer" class="navbar__item navbar__link">GitHub<svg width="13.5" height="13.5" aria-hidden="true" viewBox="0 0 24 24" class="iconExternalLink_nPIU"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></a><div class="searchBox_ZlJk"><div class="dsla-search-wrapper"><div class="dsla-search-field" data-tags="default,docs-default-current"></div></div></div></div></div><div role="presentation" class="navbar-sidebar__backdrop"></div></nav><div class="main-wrapper mainWrapper_z2l0 docsWrapper_BCFX"><button aria-label="Scroll back to top" class="clean-btn theme-back-to-top-button backToTopButton_sjWU" type="button"></button><div class="docPage__5DB"><aside class="theme-doc-sidebar-container docSidebarContainer_b6E3"><div class="sidebar_njMd"><nav class="menu thin-scrollbar menu_SIkG"><ul class="theme-doc-sidebar-menu menu__list"><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/docs">Welcome</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/docs/install">Installation</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/docs/config">Configuration</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/docs/config-reference">Configuration Reference</a></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item menu__list-item--collapsed"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist" aria-expanded="false" href="/docs/features">Features</a><button aria-label="Toggle the collapsible sidebar category 'Features'" type="button" class="clean-btn menu__caret"></button></div></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item menu__list-item--collapsed"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist" aria-expanded="false" href="/docs/usage">Usage</a><button aria-label="Toggle the collapsible sidebar category 'Usage'" type="button" class="clean-btn menu__caret"></button></div></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/docs/design">Design</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/docs/comparison">Comparison</a></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--active" aria-expanded="true" href="/docs/development">Development</a><button aria-label="Toggle the collapsible sidebar category 'Development'" type="button" class="clean-btn menu__caret"></button></div><ul style="display:block;overflow:visible;height:auto" class="menu__list"><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link menu__link--active" aria-current="page" tabindex="0" href="/docs/development/proxying">Proxying</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/docs/development/signaling">Session Signaling</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/docs/development/todo">TODOs</a></li></ul></li></ul></nav></div></aside><main class="docMainContainer_gTbr"><div class="container padding-top--md padding-bottom--lg"><div class="row"><div class="col docItemCol_VOVn"><div class="docItemContainer_Djhp"><article><nav class="theme-doc-breadcrumbs breadcrumbsContainer_Z_bl" aria-label="Breadcrumbs"><ul class="breadcrumbs" itemscope="" itemtype="https://schema.org/BreadcrumbList"><li class="breadcrumbs__item"><a aria-label="Home page" class="breadcrumbs__link" href="/"><svg viewBox="0 0 24 24" class="breadcrumbHomeIcon_OVgt"><path d="M10 19v-5h4v5c0 .55.45 1 1 1h3c.55 0 1-.45 1-1v-7h1.7c.46 0 .68-.57.33-.87L12.67 3.6c-.38-.34-.96-.34-1.34 0l-8.36 7.53c-.34.3-.13.87.33.87H5v7c0 .55.45 1 1 1h3c.55 0 1-.45 1-1z" fill="currentColor"></path></svg></a></li><li itemscope="" itemprop="itemListElement" itemtype="https://schema.org/ListItem" class="breadcrumbs__item"><a class="breadcrumbs__link" itemprop="item" href="/docs/development"><span itemprop="name">Development</span></a><meta itemprop="position" content="1"></li><li itemscope="" itemprop="itemListElement" itemtype="https://schema.org/ListItem" class="breadcrumbs__item breadcrumbs__item--active"><span class="breadcrumbs__link" itemprop="name">Proxying</span><meta itemprop="position" content="2"></li></ul></nav><div class="tocCollapsible_ETCw theme-doc-toc-mobile tocMobile_ITEo"><button type="button" class="clean-btn tocCollapsibleButton_TO0P">On this page</button></div><div class="theme-doc-markdown markdown"><h1>Proxying</h1><p>cunīcu implements multiple ways of running an ICE agent alongside WireGuard on the same UDP ports.</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="kernel-wireguard-module">Kernel WireGuard module<a class="hash-link" href="#kernel-wireguard-module" title="Direct link to heading"></a></h2><h3 class="anchor anchorWithStickyNavbar_LWe7" id="user-space">User-space<a class="hash-link" href="#user-space" title="Direct link to heading"></a></h3><p>For each WG peer a new local UDP socket is opened.
|
||
cunīcu will update the endpoint address of the peer to this the local address of the new sockets.</p><p>WireGuard traffic is proxied by cunīcu between the local UDP and the ICE socket.</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="raw-sockets--bpf-filter-kernel">RAW Sockets + BPF filter (Kernel)<a class="hash-link" href="#raw-sockets--bpf-filter-kernel" title="Direct link to heading"></a></h3><p>We allocate a single <a href="https://squidarth.com/networking/systems/rc/2018/05/28/using-raw-sockets.html" target="_blank" rel="noopener noreferrer">Linux RAW socket</a> and assign a <a href="https://riyazali.net/posts/berkeley-packet-filter-in-golang/" target="_blank" rel="noopener noreferrer">eBPF</a> filter to this socket which will only match STUN traffic to a specific UDP port.
|
||
UDP headers are parsed/produced by cunīcu.
|
||
cunīcu uses a UDPMux to mux all peers ICE Agents over this single RAW socket. </p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="nftables-port-redirection-kernel">NFtables port-redirection (Kernel)<a class="hash-link" href="#nftables-port-redirection-kernel" title="Direct link to heading"></a></h3><p>Two <a href="https://www.netfilter.org/projects/nftables/manpage.html" target="_blank" rel="noopener noreferrer">Nftables</a> (nft) rules are added to filter input & output chains respectively.
|
||
The input rule will match all non-STUN traffic directed at the local port of the ICE candidate and rewrites the UDP destination port to the local listen port of the WireGuard interface.
|
||
The output rule will mach all traffic originating from the listen port of the WG interface and directed to the port of the remote candidate and rewrites the source port to the port of the local ICE candidate. </p><p>WireGuard traffic passes only through the Netfilter chains and remains inside the kernel.
|
||
Only STUN binding requests are passed to cunīcu.</p><div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">$ </span><span class="token function" style="color:#d73a49">sudo</span><span class="token plain"> nft list ruleset</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">table inet cunicu </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> chain ingress </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token builtin class-name">type</span><span class="token plain"> filter hook input priority raw</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> policy accept</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> udp dport </span><span class="token number" style="color:#36acaa">37281</span><span class="token plain"> @th,96,32 </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">554869826</span><span class="token plain"> notrack udp dport </span><span class="token builtin class-name">set</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1001</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> chain egress </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token builtin class-name">type</span><span class="token plain"> filter hook output priority raw</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> policy accept</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> udp sport </span><span class="token number" style="color:#36acaa">1001</span><span class="token plain"> udp dport </span><span class="token number" style="color:#36acaa">38767</span><span class="token plain"> notrack udp sport </span><span class="token builtin class-name">set</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">37281</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h2 class="anchor anchorWithStickyNavbar_LWe7" id="iptables-port-redirection">IPTables port-redirection<a class="hash-link" href="#iptables-port-redirection" title="Direct link to heading"></a></h2><p>Similar to NFTables port-natting by using the legacy IPTables API.</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="user-space-wireguard-implementation">User-space WireGuard implementation<a class="hash-link" href="#user-space-wireguard-implementation" title="Direct link to heading"></a></h2><h3 class="anchor anchorWithStickyNavbar_LWe7" id="user-space-proxy">User-space Proxy<a class="hash-link" href="#user-space-proxy" title="Direct link to heading"></a></h3><p>Just like for the Kernel WireGuard module, a dedicated UDP socket for each WG peer is created.
|
||
cunīcu will update the endpoint address of the peer to this the local address of the new sockets.</p><p>WireGuard traffic is proxied by cunīcu between the local UDP and the ICE socket.</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="in-process-socket">In-process socket<a class="hash-link" href="#in-process-socket" title="Direct link to heading"></a></h3><p>cunīcu implements wireguard-go's <code>conn.Bind</code> interface to handle WireGuard's network IO.</p><p>WireGuard traffic is passed directly between <code>conn.Bind</code> and Pion's <code>ice.Conn</code>.
|
||
No round-trip through the kernel stack is required.</p><div class="theme-admonition theme-admonition-note alert alert--secondary admonition_LlT9"><div class="admonitionHeading_tbUL"><span class="admonitionIcon_kALy"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</div><div class="admonitionContent_S0QG"><p>This variant only works for the compiled-in version of wireguard-go in cunīcu.</p></div></div><h2 class="anchor anchorWithStickyNavbar_LWe7" id="flowchart">Flowchart<a class="hash-link" href="#flowchart" title="Direct link to heading"></a></h2><p><img loading="lazy" src="/assets/images/proxy-561858b0dc7f68600875c73c14ebef67.svg" width="874" height="914" class="img_ev3q"></p></div><footer class="theme-doc-footer docusaurus-mt-lg"><div class="theme-doc-footer-edit-meta-row row"><div class="col"><a href="https://github.com/stv0g/cunicu/edit/master/docs/development/proxying.md" target="_blank" rel="noreferrer noopener" class="theme-edit-this-page"><svg fill="currentColor" height="20" width="20" viewBox="0 0 40 40" class="iconEdit_Z9Sw" aria-hidden="true"><g><path d="m34.5 11.7l-3 3.1-6.3-6.3 3.1-3q0.5-0.5 1.2-0.5t1.1 0.5l3.9 3.9q0.5 0.4 0.5 1.1t-0.5 1.2z m-29.5 17.1l18.4-18.5 6.3 6.3-18.4 18.4h-6.3v-6.2z"></path></g></svg>Edit this page</a></div><div class="col lastUpdated_vwxv"></div></div></footer></article><nav class="pagination-nav docusaurus-mt-lg" aria-label="Docs pages navigation"><a class="pagination-nav__link pagination-nav__link--prev" href="/docs/development"><div class="pagination-nav__sublabel">Previous</div><div class="pagination-nav__label">Development</div></a><a class="pagination-nav__link pagination-nav__link--next" href="/docs/development/signaling"><div class="pagination-nav__sublabel">Next</div><div class="pagination-nav__label">Session Signaling</div></a></nav></div></div><div class="col col--3"><div class="tableOfContents_bqdL thin-scrollbar theme-doc-toc-desktop"><ul class="table-of-contents table-of-contents__left-border"><li><a href="#kernel-wireguard-module" class="table-of-contents__link toc-highlight">Kernel WireGuard module</a><ul><li><a href="#user-space" class="table-of-contents__link toc-highlight">User-space</a></li><li><a href="#raw-sockets--bpf-filter-kernel" class="table-of-contents__link toc-highlight">RAW Sockets + BPF filter (Kernel)</a></li><li><a href="#nftables-port-redirection-kernel" class="table-of-contents__link toc-highlight">NFtables port-redirection (Kernel)</a></li></ul></li><li><a href="#iptables-port-redirection" class="table-of-contents__link toc-highlight">IPTables port-redirection</a></li><li><a href="#user-space-wireguard-implementation" class="table-of-contents__link toc-highlight">User-space WireGuard implementation</a><ul><li><a href="#user-space-proxy" class="table-of-contents__link toc-highlight">User-space Proxy</a></li><li><a href="#in-process-socket" class="table-of-contents__link toc-highlight">In-process socket</a></li></ul></li><li><a href="#flowchart" class="table-of-contents__link toc-highlight">Flowchart</a></li></ul></div></div></div></div></main></div></div><footer class="footer footer--dark"><div class="container container-fluid"><div class="row footer__links"><div class="col footer__col"><div class="footer__title">Docs</div><ul class="footer__items clean-list"><li class="footer__item"><a class="footer__link-item" href="/docs">Tutorial</a></li></ul></div><div class="col footer__col"><div class="footer__title">Community</div><ul class="footer__items clean-list"><li class="footer__item"><a href="https://gophers.slack.com/archives/C036CTEGJFN" target="_blank" rel="noopener noreferrer" class="footer__link-item">Slack<svg width="13.5" height="13.5" aria-hidden="true" viewBox="0 0 24 24" class="iconExternalLink_nPIU"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></a></li><li class="footer__item"><a href="https://twitter.com/cunicuVPN" target="_blank" rel="noopener noreferrer" class="footer__link-item">Twitter<svg width="13.5" height="13.5" aria-hidden="true" viewBox="0 0 24 24" class="iconExternalLink_nPIU"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></a></li></ul></div><div class="col footer__col"><div class="footer__title">More</div><ul class="footer__items clean-list"><li class="footer__item"><a class="footer__link-item" href="/blog">Blog</a></li><li class="footer__item"><a href="https://github.com/stv0g/cunicu" target="_blank" rel="noopener noreferrer" class="footer__link-item">GitHub<svg width="13.5" height="13.5" aria-hidden="true" viewBox="0 0 24 24" class="iconExternalLink_nPIU"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></a></li></ul></div></div><div class="footer__bottom text--center"><div class="footer__copyright">Copyright © 2022 Institute for Automation of Complex Power Systems, RWTH Aachen University.</div></div></div></footer></div>
|
||
<script src="/assets/js/runtime~main.0d972120.js"></script>
|
||
<script src="/assets/js/main.d3aebcf9.js"></script>
|
||
</body>
|
||
</html> |