ניתוב מהיר ו-NAT בלינוקס

ככל שכתובות IPv4 מתרוקנות, מפעילי טלקום רבים מתמודדים עם הצורך לספק ללקוחותיהם גישה לרשת באמצעות תרגום כתובות. במאמר זה אספר לכם כיצד תוכלו להשיג ביצועי NAT ​​בדרגת Carrier על שרתי סחורות.

קצת היסטוריה

הנושא של מיצוי שטחי כתובות IPv4 כבר אינו חדש. בשלב מסוים, הופיעו רשימות המתנה ב-RIPE, ואז צצו בורסות שבהן נסחרו בלוקים של כתובות ונחתמו עסקאות להשכרה. בהדרגה החלו מפעילי טלקום לספק שירותי גישה לאינטרנט תוך שימוש בתרגום כתובות ונמלים. חלקם לא הצליחו להשיג מספיק כתובות כדי להנפיק כתובת "לבנה" לכל מנוי, בעוד שאחרים החלו לחסוך כסף בסירוב לרכוש כתובות בשוק המשני. יצרני ציוד רשת תמכו ברעיון זה, כי פונקציונליות זו דורשת בדרך כלל מודולי הרחבה נוספים או רישיונות. לדוגמה, בקו נתבי ה-MX של Juniper (למעט ה-MX104 וה-MX204 העדכניים ביותר), ניתן לבצע NAPT בכרטיס שירות MS-MIC נפרד, Cisco ASR1k דורש רישיון CGN, Cisco ASR9k דורש מודול A9K-ISM-100 נפרד ורישיון A9K-CGN -LIC לו. באופן כללי, התענוג עולה הרבה כסף.

IPTables

המשימה של ביצוע NAT אינה דורשת משאבי מחשוב מיוחדים; ניתן לפתור אותה על ידי מעבדים למטרות כלליות, אשר מותקנים, למשל, בכל נתב ביתי. בקנה מידה של מפעיל טלקום, ניתן לפתור בעיה זו באמצעות שרתי סחורות המריצים FreeBSD (ipfw/pf) או GNU/Linux (iptables). לא נשקול את FreeBSD, כי... הפסקתי להשתמש במערכת ההפעלה הזו די מזמן, אז נישאר ל-GNU/Linux.

הפעלת תרגום כתובות אינה קשה כלל. ראשית עליך לרשום כלל ב-iptables בטבלת nat:

iptables -t nat -A POSTROUTING -s 100.64.0.0/10 -j SNAT --to <pool_start_addr>-<pool_end_addr> --persistent

מערכת ההפעלה תטען את מודול nf_conntrack, שינטר את כל החיבורים הפעילים ויבצע את ההמרות הדרושות. יש כאן כמה דקויות. ראשית, מכיוון שאנו מדברים על NAT בקנה מידה של מפעיל טלקום, יש צורך להתאים את פסקי הזמן, כי עם ערכי ברירת מחדל, גודל טבלת התרגום יגדל במהירות לערכים קטסטרופליים. להלן דוגמה להגדרות שהשתמשתי בהן בשרתים שלי:

net.ipv4.ip_forward = 1
net.ipv4.ip_local_port_range = 8192 65535

net.netfilter.nf_conntrack_generic_timeout = 300
net.netfilter.nf_conntrack_tcp_timeout_syn_sent = 60
net.netfilter.nf_conntrack_tcp_timeout_syn_recv = 60
net.netfilter.nf_conntrack_tcp_timeout_established = 600
net.netfilter.nf_conntrack_tcp_timeout_fin_wait = 60
net.netfilter.nf_conntrack_tcp_timeout_close_wait = 45
net.netfilter.nf_conntrack_tcp_timeout_last_ack = 30
net.netfilter.nf_conntrack_tcp_timeout_time_wait = 120
net.netfilter.nf_conntrack_tcp_timeout_close = 10
net.netfilter.nf_conntrack_tcp_timeout_max_retrans = 300
net.netfilter.nf_conntrack_tcp_timeout_unacknowledged = 300
net.netfilter.nf_conntrack_udp_timeout = 30
net.netfilter.nf_conntrack_udp_timeout_stream = 60
net.netfilter.nf_conntrack_icmpv6_timeout = 30
net.netfilter.nf_conntrack_icmp_timeout = 30
net.netfilter.nf_conntrack_events_retry_timeout = 15
net.netfilter.nf_conntrack_checksum=0

ושנית, מכיוון שגודל ברירת המחדל של טבלת התרגום אינו מיועד לעבוד בתנאים של מפעיל טלקום, יש להגדיל אותו:

net.netfilter.nf_conntrack_max = 3145728

כמו כן, יש צורך להגדיל את מספר הדליים עבור טבלת ה-hash המאחסנת את כל השידורים (זוהי אפשרות במודול nf_conntrack):

options nf_conntrack hashsize=1572864

לאחר המניפולציות הפשוטות הללו, מתקבל עיצוב עובד לחלוטין שיכול לתרגם מספר רב של כתובות לקוחות למאגר של כתובות חיצוניות. עם זאת, הביצועים של פתרון זה משאירים הרבה מה לרצוי. בניסיונות הראשונים שלי להשתמש ב-GNU/Linux עבור NAT (בערך 2013), הצלחתי להשיג ביצועים של סביב 7Gbit/s ב-0.8Mpps לשרת (Xeon E5-1650v2). מאז, בוצעו אופטימיזציות רבות ושונות בערימת הרשת של ליבת GNU/Linux, הביצועים של שרת אחד על אותה חומרה עלו לכמעט 18-19 Gbit/s ב-1.8-1.9 Mpps (אלה היו המקסימום ערכים), אך הדרישה לנפח תעבורה, מעובדת על ידי שרת אחד גדלה הרבה יותר מהר. כתוצאה מכך פותחו תוכניות לאזן את העומס על שרתים שונים, אך כל זה הגביר את המורכבות של הגדרה, תחזוקה ותחזוקה של איכות השירותים הניתנים.

טבלאות NFT

כיום, טרנד אופנתי בתוכנות "החלפת תיקים" הוא השימוש ב-DPDK ו-XDP. הרבה מאמרים נכתבו בנושא זה, נאומים רבים ושונים, ומוצרים מסחריים מופיעים (למשל, SKAT מבית VasExperts). אבל בהתחשב במשאבי התכנות המוגבלים של מפעילי טלקום, זה די בעייתי ליצור כל "מוצר" המבוסס על מסגרות אלו בעצמך. יהיה הרבה יותר קשה להפעיל פתרון כזה בעתיד; במיוחד יהיה צורך לפתח כלי אבחון. לדוגמה, tcpdump סטנדרטי עם DPDK לא יעבוד סתם כך, והוא לא "יראה" מנות שנשלחות בחזרה לחוטים באמצעות XDP. בין כל הדיבורים על טכנולוגיות חדשות לפלט העברת מנות למרחב המשתמש, הם נעלמו מעיניהם דיווחים и מאמרים Pablo Neira Ayuso, מתחזק iptables, על הפיתוח של הורדת זרימה ב-nftables. בואו נסתכל על מנגנון זה ביתר פירוט.

הרעיון המרכזי הוא שאם הנתב העביר מנות מסשן אחד בשני הכיוונים של הזרימה (סשן TCP נכנס למצב ESTABLISHED), אז אין צורך להעביר מנות עוקבות של הפעלה זו דרך כל כללי חומת האש, מכיוון כל הבדיקות הללו עדיין יסתיימו עם העברת החבילה בהמשך לניתוב. ואנחנו למעשה לא צריכים לבחור מסלול - אנחנו כבר יודעים לאיזה ממשק ולאיזה מארח אנחנו צריכים לשלוח מנות בתוך הפגישה הזו. כל מה שנותר הוא לאחסן את המידע הזה ולהשתמש בו לניתוב בשלב מוקדם של עיבוד מנות. בעת ביצוע NAT, יש צורך לאחסן בנוסף מידע על שינויים בכתובות וביציאות שתורגמו על ידי מודול nf_conntrack. כן, כמובן, במקרה זה שוטרים שונים וכללי מידע וסטטיסטיים אחרים ב-iptables מפסיקים לעבוד, אבל במסגרת המשימה של NAT עומד נפרד או, למשל, גבול, זה לא כל כך חשוב, כי השירותים מופצים על פני מכשירים.

תְצוּרָה

כדי להשתמש בפונקציה הזו אנחנו צריכים:

  • השתמש בגרעין טרי. למרות העובדה שהפונקציונליות עצמה הופיעה בקרנל 4.16, במשך זמן רב היא הייתה מאוד "גולמית" וגרמה באופן קבוע לבהלת ליבה. הכל התייצב בסביבות דצמבר 2019, אז שוחררו ליבות LTS 4.19.90 ו-5.4.5.
  • כתוב מחדש כללי iptables בפורמט nftables באמצעות גרסה עדכנית למדי של nftables. עובד בדיוק בגרסה 0.9.0

אם הכל בעקרון ברור עם הנקודה הראשונה, העיקר לא לשכוח לכלול את המודול בתצורה במהלך ההרכבה (CONFIG_NFT_FLOW_OFFLOAD=m), אז הנקודה השנייה דורשת הסבר. כללי nftables מתוארים בצורה שונה לחלוטין מאשר ב-iptables. רשומות מגלה כמעט את כל הנקודות, יש גם מיוחדות ממירים כללים מ-iptables ל-nftables. לכן, אני אתן רק דוגמה של הגדרת NAT ו-flow offload. אגדה קטנה למשל: , - אלו הם ממשקי הרשת שדרכם עוברת התעבורה; במציאות יכולים להיות יותר משניים מהם. , - כתובת ההתחלה והסיום של טווח הכתובות "הלבנות".

תצורת NAT פשוטה מאוד:

#! /usr/sbin/nft -f

table nat {
        chain postrouting {
                type nat hook postrouting priority 100;
                oif <o_if> snat to <pool_addr_start>-<pool_addr_end> persistent
        }
}

עם הורדת זרימה זה קצת יותר מסובך, אבל די מובן:

#! /usr/sbin/nft -f

table inet filter {
        flowtable fastnat {
                hook ingress priority 0
                devices = { <i_if>, <o_if> }
        }

        chain forward {
                type filter hook forward priority 0; policy accept;
                ip protocol { tcp , udp } flow offload @fastnat;
        }
}

זה, למעשה, כל ההגדרה. כעת כל תעבורת TCP/UDP תיפול לטבלת fastnat ותעובד הרבה יותר מהר.

ממצאים

כדי להבהיר כמה זה "מהיר הרבה יותר", אצרף צילום מסך של העומס בשני שרתים אמיתיים, עם אותה חומרה (Xeon E5-1650v2), מוגדרת באופן זהה, תוך שימוש באותו ליבת לינוקס, אך מבצעת NAT ב-iptables (NAT4) וב-nftables (NAT5).

ניתוב מהיר ו-NAT בלינוקס

אין גרף של מנות לשנייה בצילום המסך, אבל בפרופיל הטעינה של שרתים אלה גודל החבילה הממוצע הוא בסביבות 800 בתים, כך שהערכים מגיעים עד ל-1.5 מגה שנייה. כפי שאתה יכול לראות, לשרת עם nftables יש עתודת ביצועים עצומה. נכון לעכשיו, שרת זה מעבד עד 30Gbit/s ב-3Mpps וברור שהוא מסוגל לעמוד במגבלת הרשת הפיזית של 40Gbps, תוך שהוא בעל משאבי CPU פנויים.

אני מקווה שחומר זה יהיה שימושי למהנדסי רשת המנסים לשפר את הביצועים של השרתים שלהם.

מקור: www.habr.com

הוספת תגובה