התחילו במקום שבו ההודעה מופיעה. לקוח מתחבר, המנהרה עולה, ואז הלוג מדווח שפקודת הוספת הנתיב של לינוקס נכשלה עם File exists. שום דבר במשפט הזה אינו מתייחס לתעודה, למפתח או לקובץ תצורה, אף שהמילים מרמזות על כך. הקרנל מתאר את טבלת הניתוב שלו, ולתחילית שביקשתם להתקין יש כבר רשומה.

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

מה הלקוח באמת ביקש

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

journalctl -u openvpn-client@office -b --no-pager | grep -nE 'route|File exists'
openvpn --config /etc/openvpn/client/office.ovpn --verb 4 --log /tmp/ovpn.log

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

קראו את הנתיב הקיים, לא רק את הכתובת

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

ip -4 route show table all | grep -F '10.40.'
ip -4 route show match 10.40.0.0/16
ip -4 route get 10.40.0.5

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

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

ארבעה בעלים שיכולים להיות לתחילית

נתיבים מחוברים מגיעים מכתובת שהוקצתה והמערכת יוצרת אותם מחדש בכל פעם שהכתובת הזו קיימת. לקוח DHCP או מנהל חיבורים מתקין נתיבים כחלק ממחזור החיים שלו. ייתכן שלקוח VPN אחר כבר נושא את אותה תחילית. והוק או סקריפט מקומי יכול להוסיף כזו ששום דבר אחר אינו יודע עליה.

כל בעלים דורש תיקון אחר. נתיב מחובר אינו שלכם למחיקה; הסרתו עלולה לשבור גישה מקומית לפני שהמנהרה שמישה, והקרנל יחזיר אותו. נתיב בבעלות מנהל צריך להשתנות דרך אותו מנהל, ולא עם ip route del ידני שהחיבור מחדש הבא מבטל.

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

block-local עונה על שאלה אחרת

כשפרופיל שולח את כל התעבורה דרך המנהרה, על הלקוח להחליט מה קורה לרשת המקומית. הדגל block-local משנה את ההחלטה הזו על ידי ניתוב ה-LAN המקומי דרך המנהרה תוך שמירה על החריגה המתועדת עבור שער ה-LAN; זו בחירת מדיניות תעבורה עם השלכות גלויות על הנגישות.

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

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

סגרו את הלולאה: הסירו את הכפילות במקורה

לאחר שתדעו איזה בעלים מחזיק ברשומה, הסירו את ההוראה הכפולה במקום שבו היא מוגדרת. זה יכול להיות שורת route בפרופיל, דחיפה מהשרת או הוק שכבר אינו צריך לרוץ.

ip -4 route show match 10.40.0.0/16
ip -4 route get 10.40.0.5
journalctl -u openvpn-client@office -b --no-pager | tail -n 15

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

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

מאמרים קשורים