బర్కిలీ ప్యాకెట్ ఫిల్టర్స్ (BPF) అనేది లైనక్స్ కెర్నల్ సాంకేతికత, ఇది చాలా సంవత్సరాలుగా ఆంగ్ల భాషా సాంకేతిక ప్రచురణల మొదటి పేజీలలో ఉంది. సమావేశాలు BPF యొక్క ఉపయోగం మరియు అభివృద్ధిపై నివేదికలతో నిండి ఉన్నాయి. డేవిడ్ మిల్లర్, Linux నెట్వర్క్ సబ్సిస్టమ్ మెయింటెయినర్, Linux Plumbers 2018లో తన ప్రసంగాన్ని పిలిచాడు "ఈ చర్చ XDP గురించి కాదు" (XDP అనేది BPF కోసం ఒక ఉపయోగ సందర్భం). బ్రెండన్ గ్రెగ్ అనే పేరుతో ప్రసంగాలు ఇచ్చారు Linux BPF సూపర్ పవర్స్. టోకే హాయిలాండ్-జోర్గెన్సెన్ నవ్వుతుందికెర్నల్ ఇప్పుడు మైక్రోకెర్నల్ అని. థామస్ గ్రాఫ్ అనే ఆలోచనను ప్రోత్సహిస్తుంది BPF అనేది కెర్నల్ కోసం జావాస్క్రిప్ట్.
హాబ్రేలో ఇప్పటికీ BPF యొక్క క్రమబద్ధమైన వివరణ లేదు, అందువల్ల నేను సాంకేతికత యొక్క చరిత్ర గురించి మాట్లాడటానికి ప్రయత్నిస్తాను, ఆర్కిటెక్చర్ మరియు డెవలప్మెంట్ టూల్స్ గురించి వివరించడానికి మరియు BPFని ఉపయోగించడం యొక్క అప్లికేషన్ మరియు అభ్యాస రంగాలను వివరించడానికి ప్రయత్నిస్తాను. ఈ కథనం, సున్నా, సిరీస్లో, క్లాసిక్ BPF యొక్క చరిత్ర మరియు నిర్మాణాన్ని చెబుతుంది మరియు దాని నిర్వహణ సూత్రాల రహస్యాలను కూడా వెల్లడిస్తుంది. tcpdump, seccomp, strace, ఇవే కాకండా ఇంకా.
BPF అభివృద్ధి Linux నెట్వర్కింగ్ కమ్యూనిటీచే నియంత్రించబడుతుంది, BPF యొక్క ప్రస్తుతం ఉన్న ప్రధాన అప్లికేషన్లు నెట్వర్క్లకు సంబంధించినవి మరియు అందువల్ల అనుమతితో @యూకారియోట్, నేను గొప్ప సిరీస్కి గౌరవసూచకంగా సిరీస్ని “బిపిఎఫ్ కోసం చిన్నపిల్లలు” అని పిలిచాను "చిన్న పిల్లల కోసం నెట్వర్క్లు".
BPF చరిత్రలో ఒక చిన్న కోర్సు(c)
ఆధునిక BPF సాంకేతికత అనేది పాత టెక్నాలజీకి అదే పేరుతో మెరుగైన మరియు విస్తరించిన సంస్కరణ, ఇప్పుడు గందరగోళాన్ని నివారించడానికి క్లాసిక్ BPF అని పిలుస్తారు. క్లాసిక్ BPF ఆధారంగా బాగా తెలిసిన యుటిలిటీ సృష్టించబడింది tcpdump, యంత్రాంగం seccomp, అలాగే తక్కువగా తెలిసిన మాడ్యూల్స్ xt_bpf కోసం iptables మరియు వర్గీకరణదారు cls_bpf. ఆధునిక లైనక్స్లో, క్లాసిక్ BPF ప్రోగ్రామ్లు స్వయంచాలకంగా కొత్త రూపంలోకి అనువదించబడతాయి, అయినప్పటికీ, వినియోగదారు కోణం నుండి, API స్థానంలో ఉంది మరియు క్లాసిక్ BPF కోసం కొత్త ఉపయోగాలు, మేము ఈ కథనంలో చూస్తాము, ఇప్పటికీ కనుగొనబడుతున్నాయి. ఈ కారణంగా, మరియు లైనక్స్లో క్లాసికల్ బిపిఎఫ్ అభివృద్ధి చరిత్రను అనుసరించి, అది ఎలా మరియు ఎందుకు దాని ఆధునిక రూపంలోకి పరిణామం చెందిందో స్పష్టంగా తెలుస్తుంది, నేను క్లాసికల్ బిపిఎఫ్ గురించి కథనంతో ప్రారంభించాలని నిర్ణయించుకున్నాను.
గత శతాబ్దపు ఎనభైల చివరలో, ప్రసిద్ధ లారెన్స్ బర్కిలీ లాబొరేటరీకి చెందిన ఇంజనీర్లు గత శతాబ్దపు ఎనభైల చివరలో ఆధునికమైన హార్డ్వేర్పై నెట్వర్క్ ప్యాకెట్లను ఎలా సరిగ్గా ఫిల్టర్ చేయాలనే ప్రశ్నపై ఆసక్తి కలిగి ఉన్నారు. ఫిల్టరింగ్ యొక్క ప్రాథమిక ఆలోచన, వాస్తవానికి CSPF (CMU/స్టాన్ఫోర్డ్ ప్యాకెట్ ఫిల్టర్) సాంకేతికతలో అమలు చేయబడింది, అనవసరమైన ప్యాకెట్లను వీలైనంత త్వరగా ఫిల్టర్ చేయడం, అనగా. కెర్నల్ స్పేస్లో, ఇది యూజర్ స్పేస్లోకి అనవసరమైన డేటాను కాపీ చేయడాన్ని నివారిస్తుంది. కెర్నల్ స్థలంలో వినియోగదారు కోడ్ని అమలు చేయడానికి రన్టైమ్ భద్రతను అందించడానికి, శాండ్బాక్స్డ్ వర్చువల్ మిషన్ ఉపయోగించబడింది.
అయినప్పటికీ, ఇప్పటికే ఉన్న ఫిల్టర్ల కోసం వర్చువల్ మెషీన్లు స్టాక్-ఆధారిత మెషీన్లపై అమలు చేయడానికి రూపొందించబడ్డాయి మరియు కొత్త RISC మెషీన్లలో అంత సమర్థవంతంగా పనిచేయవు. ఫలితంగా, బర్కిలీ ల్యాబ్స్ నుండి ఇంజనీర్ల కృషి ద్వారా, ఒక కొత్త BPF (బర్కిలీ ప్యాకెట్ ఫిల్టర్స్) సాంకేతికత అభివృద్ధి చేయబడింది, దీని యొక్క వర్చువల్ మెషీన్ ఆర్కిటెక్చర్ Motorola 6502 ప్రాసెసర్ ఆధారంగా రూపొందించబడింది - అటువంటి ప్రసిద్ధ ఉత్పత్తుల పని గుర్రం ఆపిల్ II లేదా NES. ఇప్పటికే ఉన్న సొల్యూషన్లతో పోలిస్తే కొత్త వర్చువల్ మెషీన్ ఫిల్టర్ పనితీరును పదుల రెట్లు పెంచింది.
BPF మెషిన్ ఆర్కిటెక్చర్
ఉదాహరణలను విశ్లేషిస్తూ, పని చేసే పద్ధతిలో మేము ఆర్కిటెక్చర్తో పరిచయం పొందుతాము. అయితే, ప్రారంభించడానికి, మెషీన్లో రెండు 32-బిట్ రిజిస్టర్లు యూజర్కి అందుబాటులో ఉన్నాయని చెప్పండి, ఒక అక్యుమ్యులేటర్ A మరియు ఇండెక్స్ రిజిస్టర్ X, 64 బైట్ల మెమరీ (16 పదాలు), రాయడం మరియు తదుపరి పఠనం కోసం అందుబాటులో ఉన్నాయి మరియు ఈ వస్తువులతో పని చేయడానికి కమాండ్ల యొక్క చిన్న వ్యవస్థ. షరతులతో కూడిన వ్యక్తీకరణలను అమలు చేయడానికి జంప్ సూచనలు ప్రోగ్రామ్లలో కూడా అందుబాటులో ఉన్నాయి, అయితే ప్రోగ్రామ్ యొక్క సకాలంలో పూర్తి చేయడానికి హామీ ఇవ్వడానికి, జంప్లు ముందుకు మాత్రమే చేయబడతాయి, అనగా, ప్రత్యేకించి, లూప్లను సృష్టించడం నిషేధించబడింది.
యంత్రాన్ని ప్రారంభించడానికి సాధారణ పథకం క్రింది విధంగా ఉంటుంది. వినియోగదారు BPF ఆర్కిటెక్చర్ కోసం ఒక ప్రోగ్రామ్ను సృష్టిస్తాడు మరియు ఉపయోగించి కొన్ని కెర్నల్ మెకానిజం (సిస్టమ్ కాల్ వంటివి), ప్రోగ్రామ్ను లోడ్ చేస్తుంది మరియు కనెక్ట్ చేస్తుంది కొందరికి కెర్నల్లోని ఈవెంట్ జనరేటర్కు (ఉదాహరణకు, ఈవెంట్ అంటే నెట్వర్క్ కార్డ్లోని తదుపరి ప్యాకెట్ రాక). ఒక ఈవెంట్ సంభవించినప్పుడు, కెర్నల్ ప్రోగ్రామ్ను నడుపుతుంది (ఉదాహరణకు, ఇంటర్ప్రెటర్లో), మరియు మెషిన్ మెమరీ దీనికి అనుగుణంగా ఉంటుంది కొందరికి కెర్నల్ మెమరీ ప్రాంతం (ఉదాహరణకు, ఇన్కమింగ్ ప్యాకెట్ యొక్క డేటా).
ఉదాహరణలను చూడటం ప్రారంభించడానికి పైన పేర్కొన్నవి సరిపోతాయి: మేము సిస్టమ్ మరియు కమాండ్ ఫార్మాట్తో అవసరమైన విధంగా పరిచయం చేస్తాము. మీరు వర్చువల్ మెషీన్ యొక్క కమాండ్ సిస్టమ్ను వెంటనే అధ్యయనం చేయాలనుకుంటే మరియు దాని అన్ని సామర్థ్యాల గురించి తెలుసుకోవాలనుకుంటే, మీరు అసలు కథనాన్ని చదవవచ్చు BSD ప్యాకెట్ ఫిల్టర్ మరియు/లేదా ఫైల్ మొదటి సగం డాక్యుమెంటేషన్/నెట్వర్కింగ్/filter.txt కెర్నల్ డాక్యుమెంటేషన్ నుండి. అదనంగా, మీరు ప్రదర్శనను అధ్యయనం చేయవచ్చు libpcap: ప్యాకెట్ క్యాప్చర్ కోసం ఆర్కిటెక్చర్ మరియు ఆప్టిమైజేషన్ మెథడాలజీ, దీనిలో BPF రచయితలలో ఒకరైన మక్కన్ సృష్టి చరిత్ర గురించి మాట్లాడాడు libpcap.
మేము Linuxలో క్లాసిక్ BPFని ఉపయోగించడం యొక్క అన్ని ముఖ్యమైన ఉదాహరణలను పరిగణలోకి తీసుకుంటాము: tcpdump (libpcap), సెకాంప్, xt_bpf, cls_bpf.
tcpdump
BPF అభివృద్ధి ప్యాకెట్ ఫిల్టరింగ్ కోసం ఫ్రంటెండ్ అభివృద్ధికి సమాంతరంగా నిర్వహించబడింది - ఇది బాగా తెలిసిన యుటిలిటీ tcpdump. మరియు, ఇది అనేక ఆపరేటింగ్ సిస్టమ్లలో అందుబాటులో ఉన్న క్లాసిక్ BPFని ఉపయోగించడం యొక్క పురాతన మరియు అత్యంత ప్రసిద్ధ ఉదాహరణ కాబట్టి, మేము దానితో సాంకేతికతపై మా అధ్యయనాన్ని ప్రారంభిస్తాము.
(నేను Linuxలో ఈ కథనంలోని అన్ని ఉదాహరణలను అమలు చేసాను 5.6.0-rc6. మెరుగైన రీడబిలిటీ కోసం కొన్ని ఆదేశాల అవుట్పుట్ సవరించబడింది.)
ఉదాహరణ: IPv6 ప్యాకెట్లను గమనించడం
మనం ఇంటర్ఫేస్లో అన్ని IPv6 ప్యాకెట్లను చూడాలనుకుంటున్నామని ఊహించుకుందాం eth0. దీన్ని చేయడానికి, మేము ప్రోగ్రామ్ను అమలు చేయవచ్చు tcpdump సాధారణ ఫిల్టర్తో ip6:
$ sudo tcpdump -i eth0 ip6
అందువలన tcpdump ఫిల్టర్ను కంపైల్ చేస్తుంది ip6 BPF ఆర్కిటెక్చర్ బైట్కోడ్లోకి మరియు దానిని కెర్నల్కు పంపండి (విభాగంలోని వివరాలను చూడండి Tcpdump: లోడ్ అవుతోంది) ఇంటర్ఫేస్ గుండా వెళుతున్న ప్రతి ప్యాకెట్కు లోడ్ చేయబడిన ఫిల్టర్ అమలు చేయబడుతుంది eth0. ఫిల్టర్ సున్నా కాని విలువను అందిస్తే n, ఆపై వరకు n ప్యాకెట్ యొక్క బైట్లు వినియోగదారు స్థలానికి కాపీ చేయబడతాయి మరియు మేము దానిని అవుట్పుట్లో చూస్తాము tcpdump.
కెర్నల్కు ఏ బైట్కోడ్ పంపబడిందో మనం సులభంగా కనుగొనవచ్చు tcpdump సహాయంతో tcpdump, మేము దానిని ఎంపికతో అమలు చేస్తే -d:
$ sudo tcpdump -i eth0 -d ip6
(000) ldh [12]
(001) jeq #0x86dd jt 2 jf 3
(002) ret #262144
(003) ret #0
లైన్ సున్నాలో మేము ఆదేశాన్ని అమలు చేస్తాము ldh [12], అంటే “లోడ్ ఇన్ రిజిస్టర్ A సగం పదం (16 బిట్లు) చిరునామా 12” వద్ద ఉంది మరియు ఒకే ప్రశ్న ఏమిటంటే మనం ఎలాంటి మెమరీని సంబోధిస్తున్నాము? సమాధానం వద్ద ఉంది x ప్రారంభమవుతుంది (x+1)విశ్లేషించబడిన నెట్వర్క్ ప్యాకెట్ యొక్క వ బైట్. మేము ఈథర్నెట్ ఇంటర్ఫేస్ నుండి ప్యాకెట్లను చదువుతాము eth0, మరియు ఇది అంటేప్యాకెట్ ఇలా కనిపిస్తుంది (సరళత కోసం, ప్యాకెట్లో VLAN ట్యాగ్లు లేవని మేము అనుకుంటాము):
6 6 2
|Destination MAC|Source MAC|Ether Type|...|
కాబట్టి ఆదేశాన్ని అమలు చేసిన తర్వాత ldh [12] రిజిస్టర్ లో A ఒక క్షేత్రం ఉంటుంది Ether Type — ఈ ఈథర్నెట్ ఫ్రేమ్లో ప్రసారం చేయబడిన ప్యాకెట్ రకం. లైన్ 1లో మేము రిజిస్టర్లోని విషయాలను సరిపోల్చాము A (ప్యాకేజీ రకం) సి 0x86dd, మరియు ఇది మరియు ఉంది మాకు ఆసక్తి ఉన్న రకం IPv6. లైన్ 1లో, పోలిక కమాండ్తో పాటు, మరో రెండు నిలువు వరుసలు ఉన్నాయి - jt 2 и jf 3 - పోలిక విజయవంతమైతే మీరు వెళ్లవలసిన మార్కులు (A == 0x86dd) మరియు విజయవంతం కాలేదు. కాబట్టి, విజయవంతమైన సందర్భంలో (IPv6) మేము లైన్ 2కి వెళ్తాము మరియు విజయవంతం కాని సందర్భంలో - లైన్ 3కి. లైన్ 3లో ప్రోగ్రామ్ కోడ్ 0తో ముగుస్తుంది (ప్యాకెట్ను కాపీ చేయవద్దు), లైన్ 2లో ప్రోగ్రామ్ కోడ్తో ముగుస్తుంది. 262144 (నాకు గరిష్టంగా 256 కిలోబైట్ల ప్యాకేజీని కాపీ చేయండి).
మరింత సంక్లిష్టమైన ఉదాహరణ: మేము గమ్య పోర్ట్ ద్వారా TCP ప్యాకెట్లను చూస్తాము
డెస్టినేషన్ పోర్ట్ 666తో అన్ని TCP ప్యాకెట్లను కాపీ చేసే ఫిల్టర్ ఎలా ఉంటుందో చూద్దాం. IPv4 కేస్ సరళమైనది కాబట్టి మేము IPv6 కేస్ను పరిశీలిస్తాము. ఈ ఉదాహరణను అధ్యయనం చేసిన తర్వాత, మీరు IPv6 ఫిల్టర్ని మీరే ఒక వ్యాయామంగా అన్వేషించవచ్చు (ip6 and tcp dst port 666) మరియు సాధారణ కేసు కోసం ఫిల్టర్ (tcp dst port 666) కాబట్టి, మనకు ఆసక్తి ఉన్న ఫిల్టర్ ఇలా కనిపిస్తుంది:
$ sudo tcpdump -i eth0 -d ip and tcp dst port 666
(000) ldh [12]
(001) jeq #0x800 jt 2 jf 10
(002) ldb [23]
(003) jeq #0x6 jt 4 jf 10
(004) ldh [20]
(005) jset #0x1fff jt 10 jf 6
(006) ldxb 4*([14]&0xf)
(007) ldh [x + 16]
(008) jeq #0x29a jt 9 jf 10
(009) ret #262144
(010) ret #0
0 మరియు 1 పంక్తులు ఏమి చేస్తాయో మాకు ఇప్పటికే తెలుసు. లైన్ 2లో ఇది IPv4 ప్యాకెట్ అని మేము ఇప్పటికే తనిఖీ చేసాము (ఈథర్ టైప్ = 0x800) మరియు దానిని రిజిస్టర్లో లోడ్ చేయండి A ప్యాకెట్ యొక్క 24వ బైట్. మా ప్యాకేజీ కనిపిస్తోంది
అంటే మనం రిజిస్టర్లోకి లోడ్ చేస్తాము A IP హెడర్ యొక్క ప్రోటోకాల్ ఫీల్డ్, ఇది లాజికల్, ఎందుకంటే మేము TCP ప్యాకెట్లను మాత్రమే కాపీ చేయాలనుకుంటున్నాము. మేము ప్రోటోకాల్తో పోల్చాము 0x6 (IPPROTO_TCP) లైన్ 3 లో.
4 మరియు 5 లైన్లలో మేము చిరునామా 20 వద్ద ఉన్న సగం పదాలను లోడ్ చేస్తాము మరియు ఆదేశాన్ని ఉపయోగిస్తాము jset మూడింటిలో ఒకటి సెట్ చేయబడిందో లేదో తనిఖీ చేయండి జెండాలు - ముసుగు ధరించి జారీ చేయబడింది jset మూడు అత్యంత ముఖ్యమైన బిట్లు క్లియర్ చేయబడ్డాయి. మూడు బిట్లలో రెండు, ప్యాకెట్ ఫ్రాగ్మెంటెడ్ IP ప్యాకెట్లో భాగమా మరియు అలా అయితే, అది చివరి ఫ్రాగ్మెంట్ కాదా అని మాకు తెలియజేస్తాయి. మూడవ బిట్ రిజర్వ్ చేయబడింది మరియు తప్పనిసరిగా సున్నా అయి ఉండాలి. మేము అసంపూర్ణమైన లేదా విరిగిన ప్యాకెట్లను తనిఖీ చేయకూడదనుకుంటున్నాము, కాబట్టి మేము మూడు బిట్లను తనిఖీ చేస్తాము.
ఈ జాబితాలో 6వ పంక్తి అత్యంత ఆసక్తికరమైనది. వ్యక్తీకరణ ldxb 4*([14]&0xf) అంటే మనం రిజిస్టర్లోకి లోడ్ చేస్తాము X ప్యాకెట్లోని పదిహేనవ బైట్లో అతి తక్కువ ముఖ్యమైన నాలుగు బిట్లు 4తో గుణించబడతాయి. పదిహేనవ బైట్లో అతి తక్కువ ముఖ్యమైన నాలుగు బిట్లు ఫీల్డ్ ఇంటర్నెట్ హెడర్ పొడవు IPv4 హెడర్, ఇది హెడర్ యొక్క పొడవును పదాలలో నిల్వ చేస్తుంది, కాబట్టి మీరు 4 ద్వారా గుణించాలి. ఆసక్తికరంగా, వ్యక్తీకరణ 4*([14]&0xf) అనేది ఈ ఫారమ్లో మాత్రమే మరియు రిజిస్టర్ కోసం మాత్రమే ఉపయోగించబడే ప్రత్యేక అడ్రసింగ్ స్కీమ్ కోసం ఒక హోదా X, అనగా మేము కూడా చెప్పలేము ldb 4*([14]&0xf) లేదా ldxb 5*([14]&0xf) (మేము వేరే ఆఫ్సెట్ను మాత్రమే పేర్కొనగలము, ఉదాహరణకు, ldxb 4*([16]&0xf)) ఈ అడ్రసింగ్ స్కీమ్ ఖచ్చితంగా అందుకోవడానికి BPFకి జోడించబడిందని స్పష్టంగా తెలుస్తుంది X (ఇండెక్స్ రిజిస్టర్) IPv4 హెడర్ పొడవు.
కాబట్టి లైన్ 7లో మేము సగం పదాన్ని లోడ్ చేయడానికి ప్రయత్నిస్తాము (X+16). ఈథర్నెట్ హెడర్ ద్వారా 14 బైట్లు ఆక్రమించబడి ఉన్నాయని గుర్తుంచుకోండి మరియు X IPv4 హెడర్ యొక్క పొడవును కలిగి ఉంది, మేము దానిని అర్థం చేసుకున్నాము A TCP డెస్టినేషన్ పోర్ట్ లోడ్ చేయబడింది:
14 X 2 2
|ethernet header|ip header|source port|destination port|
చివరగా, లైన్ 8లో మేము కావలసిన విలువతో డెస్టినేషన్ పోర్ట్ను సరిపోల్చాము మరియు 9 లేదా 10 లైన్లలో మేము ఫలితాన్ని తిరిగి ఇస్తాము - ప్యాకెట్ను కాపీ చేయాలా వద్దా.
Tcpdump: లోడ్ అవుతోంది
మునుపటి ఉదాహరణలలో, ప్యాకెట్ ఫిల్టరింగ్ కోసం మేము BPF బైట్కోడ్ని కెర్నల్లోకి ఎలా లోడ్ చేస్తాము అనే దానిపై మేము ప్రత్యేకంగా వివరంగా చెప్పలేదు. సాధారణంగా చెప్పాలంటే, tcpdump అనేక సిస్టమ్లకు మరియు ఫిల్టర్లతో పని చేయడానికి పోర్ట్ చేయబడింది tcpdump లైబ్రరీని ఉపయోగిస్తుంది libpcap. క్లుప్తంగా, ఉపయోగించి ఇంటర్ఫేస్లో ఫిల్టర్ను ఉంచడానికి libpcap, మీరు ఈ క్రింది వాటిని చేయాలి:
టైప్ డిస్క్రిప్టర్ని సృష్టించండి pcap_t ఇంటర్ఫేస్ పేరు నుండి: pcap_create,
అవుట్పుట్ యొక్క మొదటి రెండు పంక్తులలో మేము సృష్టిస్తాము ముడి సాకెట్ అన్ని ఈథర్నెట్ ఫ్రేమ్లను చదవడానికి మరియు దానిని ఇంటర్ఫేస్కు బంధించడానికి eth0. నుండి మా మొదటి ఉదాహరణ ఫిల్టర్ అని మాకు తెలుసు ip నాలుగు BPF సూచనలను కలిగి ఉంటుంది మరియు మూడవ లైన్లో ఎంపికను ఎలా ఉపయోగించాలో చూద్దాం SO_ATTACH_FILTER సిస్టమ్ కాల్ setsockopt మేము పొడవు 4 ఫిల్టర్ను లోడ్ చేసి కనెక్ట్ చేస్తాము. ఇది మా ఫిల్టర్.
క్లాసిక్ BPFలో, ఫిల్టర్ను లోడ్ చేయడం మరియు కనెక్ట్ చేయడం ఎల్లప్పుడూ అటామిక్ ఆపరేషన్గా జరుగుతుందని గమనించాలి మరియు BPF యొక్క కొత్త వెర్షన్లో, ప్రోగ్రామ్ను లోడ్ చేయడం మరియు ఈవెంట్ జనరేటర్కు బైండింగ్ చేయడం సమయానికి వేరు చేయబడుతుంది.
దాచిన నిజం
అవుట్పుట్ యొక్క కొంచెం పూర్తి వెర్షన్ ఇలా కనిపిస్తుంది:
పైన చెప్పినట్లుగా, మేము 5 వ లైన్లోని సాకెట్కు మా ఫిల్టర్ను లోడ్ చేసి కనెక్ట్ చేస్తాము, అయితే 3 మరియు 4 లైన్లలో ఏమి జరుగుతుంది? ఇదీ అని తేలింది libpcap మమ్మల్ని జాగ్రత్తగా చూసుకుంటుంది - తద్వారా మా ఫిల్టర్ అవుట్పుట్లో సంతృప్తి చెందని ప్యాకెట్లు, లైబ్రరీ ఉండవు కలుపుతుంది నకిలీ ఫిల్టర్ ret #0 (అన్ని ప్యాకెట్లను వదలండి), సాకెట్ను నాన్-బ్లాకింగ్ మోడ్కి మారుస్తుంది మరియు మునుపటి ఫిల్టర్ల నుండి మిగిలి ఉన్న అన్ని ప్యాకెట్లను తీసివేయడానికి ప్రయత్నిస్తుంది.
మొత్తంగా, క్లాసిక్ BPFని ఉపయోగించి Linuxలో ప్యాకేజీలను ఫిల్టర్ చేయడానికి, మీరు నిర్మాణ రూపంలో ఫిల్టర్ని కలిగి ఉండాలి struct sock_fprog మరియు ఓపెన్ సాకెట్, దీని తర్వాత ఫిల్టర్ని సిస్టమ్ కాల్ని ఉపయోగించి సాకెట్కు జోడించవచ్చు setsockopt.
ఆసక్తికరంగా, ఫిల్టర్ ముడికి మాత్రమే కాకుండా ఏదైనా సాకెట్కు జోడించబడుతుంది. ఇక్కడ ఒక ఉదాహరణ అన్ని ఇన్కమింగ్ UDP డేటాగ్రామ్ల నుండి మొదటి రెండు బైట్లను మినహాయించి అన్నింటినీ కత్తిరించే ప్రోగ్రామ్. (వ్యాసాన్ని చిందరవందర చేయకూడదని నేను కోడ్లో వ్యాఖ్యలను జోడించాను.)
ఉపయోగం గురించి మరిన్ని వివరాలు setsockopt ఫిల్టర్లను కనెక్ట్ చేయడానికి, చూడండి సాకెట్ (7), కానీ మీ స్వంత ఫిల్టర్లను వ్రాయడం గురించి struct sock_fprog సహాయం లేకుండా tcpdump మేము విభాగంలో మాట్లాడుతాము మా స్వంత చేతులతో BPF ప్రోగ్రామింగ్.
క్లాసిక్ BPF మరియు 21వ శతాబ్దం
BPF 1997లో Linuxలో చేర్చబడింది మరియు చాలా కాలం పాటు పని చేసే వ్యక్తిగా ఉంది libpcap ఎటువంటి ప్రత్యేక మార్పులు లేకుండా (Linux-నిర్దిష్ట మార్పులు, అయితే, ఇది, కానీ వారు ప్రపంచ చిత్రాన్ని మార్చలేదు). 2011లో ఎరిక్ డుమాజెట్ ప్రతిపాదించినప్పుడు BPF అభివృద్ధి చెందుతుందనే మొదటి తీవ్రమైన సంకేతాలు వచ్చాయి. పాచ్, ఇది జస్ట్ ఇన్ టైమ్ కంపైలర్ను కెర్నల్కు జోడిస్తుంది - BPF బైట్కోడ్ను స్థానికంగా మార్చడానికి అనువాదకుడు x86_64 కోడ్.
మార్పుల గొలుసులో JIT కంపైలర్ మొదటిది: 2012లో కనిపించాడు కోసం ఫిల్టర్లను వ్రాయగల సామర్థ్యం సెకాంప్, BPF ఉపయోగించి, జనవరి 2013లో ఉంది జోడించబడింది మాడ్యూల్ xt_bpf, ఇది నియమాలను వ్రాయడానికి మిమ్మల్ని అనుమతిస్తుంది iptables BPF సహాయంతో, మరియు అక్టోబర్ 2013 లో జోడించబడింది ఒక మాడ్యూల్ కూడా cls_bpf, ఇది BPFని ఉపయోగించి ట్రాఫిక్ వర్గీకరణలను వ్రాయడానికి మిమ్మల్ని అనుమతిస్తుంది.
మేము త్వరలో ఈ ఉదాహరణలన్నింటిని మరింత వివరంగా పరిశీలిస్తాము, అయితే మొదట లైబ్రరీ అందించిన సామర్థ్యాల నుండి BPF కోసం ఏకపక్ష ప్రోగ్రామ్లను ఎలా వ్రాయాలో మరియు కంపైల్ చేయాలో తెలుసుకోవడానికి ఇది ఉపయోగకరంగా ఉంటుంది. libpcap పరిమిత (సాధారణ ఉదాహరణ: ఫిల్టర్ రూపొందించబడింది libpcap రెండు విలువలను మాత్రమే తిరిగి ఇవ్వగలదు - 0 లేదా 0x40000) లేదా సాధారణంగా, సెకాంప్ విషయంలో వర్తించదు.
మా స్వంత చేతులతో BPF ప్రోగ్రామింగ్
BPF సూచనల బైనరీ ఫార్మాట్తో పరిచయం చేసుకుందాం, ఇది చాలా సులభం:
16 8 8 32
| code | jt | jf | k |
ప్రతి సూచన 64 బిట్లను ఆక్రమిస్తుంది, దీనిలో మొదటి 16 బిట్లు సూచన కోడ్, తర్వాత రెండు ఎనిమిది-బిట్ ఇండెంట్లు ఉన్నాయి, jt и jf, మరియు వాదన కోసం 32 బిట్లు K, దీని ప్రయోజనం ఆదేశం నుండి ఆదేశానికి మారుతుంది. ఉదాహరణకు, ఆదేశం ret, ఇది ప్రోగ్రామ్ను ముగించే కోడ్ను కలిగి ఉంటుంది 6, మరియు రిటర్న్ విలువ స్థిరాంకం నుండి తీసుకోబడుతుంది K. Cలో, ఒకే BPF సూచన ఒక నిర్మాణంగా సూచించబడుతుంది
మెషిన్ కోడ్ల రూపంలో ప్రోగ్రామ్లను వ్రాయడం చాలా సౌకర్యవంతంగా ఉండదు, కానీ కొన్నిసార్లు ఇది అవసరం (ఉదాహరణకు, డీబగ్గింగ్, యూనిట్ పరీక్షలను సృష్టించడం, హబ్రేపై కథనాలు రాయడం మొదలైనవి). సౌలభ్యం కోసం, ఫైల్లో <linux/filter.h> సహాయక మాక్రోలు నిర్వచించబడ్డాయి - పైన పేర్కొన్న అదే ఉదాహరణను ఇలా తిరిగి వ్రాయవచ్చు
అయితే, ఈ ఎంపిక చాలా సౌకర్యవంతంగా లేదు. ఇది Linux కెర్నల్ ప్రోగ్రామర్లు తర్కించారు, అందువలన డైరెక్టరీలో tools/bpf కెర్నలు మీరు క్లాసిక్ BPFతో పని చేయడానికి అసెంబ్లర్ మరియు డీబగ్గర్ను కనుగొనవచ్చు.
అసెంబ్లీ భాష డీబగ్ అవుట్పుట్కి చాలా పోలి ఉంటుంది tcpdump, కానీ అదనంగా మేము సింబాలిక్ లేబుల్లను పేర్కొనవచ్చు. ఉదాహరణకు, TCP/IPv4 మినహా అన్ని ప్యాకెట్లను డ్రాప్ చేసే ప్రోగ్రామ్ ఇక్కడ ఉంది:
$ cat /tmp/tcp-over-ipv4.bpf
ldh [12]
jne #0x800, drop
ldb [23]
jneq #6, drop
ret #-1
drop: ret #0
డిఫాల్ట్గా, అసెంబ్లర్ ఫార్మాట్లో కోడ్ను రూపొందిస్తుంది <количество инструкций>,<code1> <jt1> <jf1> <k1>,..., TCPతో మా ఉదాహరణ కోసం ఇది ఉంటుంది
ఈ వచనాన్ని టైప్ స్ట్రక్చర్ డెఫినిషన్లోకి కాపీ చేయవచ్చు struct sock_filter, మేము ఈ విభాగం ప్రారంభంలో చేసినట్లుగా.
Linux మరియు netsniff-ng పొడిగింపులు
ప్రామాణిక BPFతో పాటు, Linux మరియు tools/bpf/bpf_asm మద్దతు మరియు ప్రామాణికం కాని సెట్. ప్రాథమికంగా, నిర్మాణం యొక్క ఫీల్డ్లను యాక్సెస్ చేయడానికి సూచనలు ఉపయోగించబడతాయి struct sk_buff, ఇది కెర్నల్లోని నెట్వర్క్ ప్యాకెట్ను వివరిస్తుంది. అయితే, ఇతర రకాల సహాయక సూచనలు కూడా ఉన్నాయి, ఉదాహరణకు ldw cpu రిజిస్టర్లో లోడ్ అవుతుంది A కెర్నల్ ఫంక్షన్ని అమలు చేయడం వల్ల ఫలితం raw_smp_processor_id(). (BPF యొక్క కొత్త వెర్షన్లో, మెమరీ, నిర్మాణాలు మరియు ఈవెంట్లను సృష్టించడం కోసం కెర్నల్ సహాయకుల సమితితో ప్రోగ్రామ్లను అందించడానికి ఈ ప్రామాణికం కాని పొడిగింపులు విస్తరించబడ్డాయి.) ఇక్కడ మేము ఫిల్టర్ని మాత్రమే కాపీ చేసే ఒక ఆసక్తికరమైన ఉదాహరణ పొడిగింపును ఉపయోగించి వినియోగదారు స్థలంలోకి ప్యాకెట్ శీర్షికలు poff, పేలోడ్ ఆఫ్సెట్:
ld poff
ret a
BPF పొడిగింపులు ఉపయోగించబడవు tcpdump, కానీ యుటిలిటీ ప్యాకేజీతో పరిచయం పొందడానికి ఇది మంచి కారణం netsniff-ng, ఇది ఇతర విషయాలతోపాటు, అధునాతన ప్రోగ్రామ్ను కలిగి ఉంటుంది netsniff-ng, ఇది, BPFని ఉపయోగించి ఫిల్టరింగ్తో పాటు, సమర్థవంతమైన ట్రాఫిక్ జనరేటర్ను కూడా కలిగి ఉంది మరియు దాని కంటే అధునాతనమైనది tools/bpf/bpf_asm, ఒక BPF అసెంబ్లర్ పిలిచారు bpfc. ప్యాకేజీ చాలా వివరణాత్మక డాక్యుమెంటేషన్ను కలిగి ఉంది, వ్యాసం చివరిలో ఉన్న లింక్లను కూడా చూడండి.
సెకాంప్
కాబట్టి, ఏకపక్ష సంక్లిష్టత యొక్క BPF ప్రోగ్రామ్లను ఎలా వ్రాయాలో మాకు ఇప్పటికే తెలుసు మరియు కొత్త ఉదాహరణలను చూడటానికి సిద్ధంగా ఉన్నాము, వీటిలో మొదటిది seccomp సాంకేతికత, ఇది BPF ఫిల్టర్లను ఉపయోగించి, సిస్టమ్ కాల్ ఆర్గ్యుమెంట్ల సెట్ మరియు సెట్ను నిర్వహించడానికి అనుమతిస్తుంది. ఇచ్చిన ప్రక్రియ మరియు దాని వారసులు.
seccomp యొక్క మొదటి సంస్కరణ 2005లో కెర్నల్కు జోడించబడింది మరియు ఇది చాలా ప్రజాదరణ పొందలేదు, ఎందుకంటే ఇది ఒకే ఎంపికను మాత్రమే అందించింది - కింది వాటికి అందుబాటులో ఉన్న సిస్టమ్ కాల్ల సెట్ను పరిమితం చేయడానికి: read, write, exit и sigreturn, మరియు నిబంధనలను ఉల్లంఘించిన ప్రక్రియ ఉపయోగించి చంపబడింది SIGKILL. అయినప్పటికీ, 2012లో, seccomp BPF ఫిల్టర్లను ఉపయోగించగల సామర్థ్యాన్ని జోడించింది, ఇది అనుమతించబడిన సిస్టమ్ కాల్ల సమితిని నిర్వచించడానికి మరియు వాటి వాదనలపై తనిఖీలను కూడా చేయడానికి మిమ్మల్ని అనుమతిస్తుంది. (ఆసక్తికరంగా, ఈ ఫంక్షనాలిటీ యొక్క మొదటి వినియోగదారులలో Chrome ఒకరు, మరియు Chrome వ్యక్తులు ప్రస్తుతం BPF యొక్క కొత్త వెర్షన్ ఆధారంగా KRSI మెకానిజంను అభివృద్ధి చేస్తున్నారు మరియు Linux సెక్యూరిటీ మాడ్యూల్స్ యొక్క అనుకూలీకరణను అనుమతిస్తుంది.) అదనపు డాక్యుమెంటేషన్కు లింక్లను చివరిలో చూడవచ్చు. వ్యాసం యొక్క.
సెకాంప్ను ఉపయోగించడం గురించి హబ్లో ఇప్పటికే కథనాలు ఉన్నాయని గమనించండి, ఎవరైనా ఈ క్రింది ఉపవిభాగాలను చదవడానికి ముందు (లేదా బదులుగా) వాటిని చదవాలనుకుంటున్నారు. వ్యాసంలో కంటైనర్లు మరియు భద్రత: seccomp 2007 వెర్షన్ మరియు BPF (ఫిల్టర్లు libseccomp ఉపయోగించి రూపొందించబడ్డాయి) ఉపయోగించి seccomp ఉపయోగించి ఉదాహరణలను అందిస్తుంది, డాకర్తో seccomp యొక్క కనెక్షన్ గురించి మాట్లాడుతుంది మరియు అనేక ఉపయోగకరమైన లింక్లను కూడా అందిస్తుంది. వ్యాసంలో systemdతో డెమోన్లను వేరుచేయడం లేదా "దీని కోసం మీకు డాకర్ అవసరం లేదు!" ఇది డెమోన్లు నడుస్తున్న systemd కోసం బ్లాక్లిస్ట్లు లేదా సిస్టమ్ కాల్ల వైట్లిస్ట్లను ఎలా జోడించాలో వివరిస్తుంది.
తరువాత ఫిల్టర్లను ఎలా వ్రాయాలి మరియు లోడ్ చేయాలో చూద్దాం seccomp బేర్ C లో మరియు లైబ్రరీని ఉపయోగిస్తున్నారు libseccomp మరియు ప్రతి ఎంపిక యొక్క లాభాలు మరియు నష్టాలు ఏమిటి మరియు చివరగా, ప్రోగ్రామ్ ద్వారా seccomp ఎలా ఉపయోగించబడుతుందో చూద్దాం strace.
సెకాంప్ కోసం ఫిల్టర్లను వ్రాయడం మరియు లోడ్ చేయడం
BPF ప్రోగ్రామ్లను ఎలా వ్రాయాలో మాకు ఇప్పటికే తెలుసు, కాబట్టి మొదట seccomp ప్రోగ్రామింగ్ ఇంటర్ఫేస్ను చూద్దాం. మీరు ప్రాసెస్ స్థాయిలో ఫిల్టర్ని సెట్ చేయవచ్చు మరియు అన్ని చైల్డ్ ప్రాసెస్లు పరిమితులను వారసత్వంగా పొందుతాయి. ఇది సిస్టమ్ కాల్ ఉపయోగించి చేయబడుతుంది seccomp(2):
seccomp(SECCOMP_SET_MODE_FILTER, flags, &filter)
పేరు &filter - ఇది మనకు ఇప్పటికే సుపరిచితమైన నిర్మాణానికి పాయింటర్ struct sock_fprog, అనగా BPF కార్యక్రమం.
seccomp కోసం ప్రోగ్రామ్లు సాకెట్ల ప్రోగ్రామ్ల నుండి ఎలా భిన్నంగా ఉంటాయి? ప్రసారం చేయబడిన సందర్భం. సాకెట్ల విషయంలో, ప్యాకెట్ను కలిగి ఉన్న మెమరీ ప్రాంతాన్ని మాకు అందించారు మరియు సెకాంప్ విషయంలో మనకు ఇలాంటి నిర్మాణం ఇవ్వబడింది
ఇది nr ప్రారంభించాల్సిన సిస్టమ్ కాల్ నంబర్, arch - ప్రస్తుత నిర్మాణం (దీనిపై మరింత దిగువన), args - ఆరు వరకు సిస్టమ్ కాల్ వాదనలు, మరియు instruction_pointer సిస్టమ్ కాల్ని చేసిన యూజర్ స్పేస్ సూచనకు పాయింటర్. అందువలన, ఉదాహరణకు, సిస్టమ్ కాల్ నంబర్ను రిజిస్టర్లోకి లోడ్ చేయడానికి A మనం చెప్పాలి
ldw [0]
సెకాంప్ ప్రోగ్రామ్ల కోసం ఇతర ఫీచర్లు ఉన్నాయి, ఉదాహరణకు, సందర్భాన్ని 32-బిట్ అలైన్మెంట్ ద్వారా మాత్రమే యాక్సెస్ చేయవచ్చు మరియు మీరు ఫిల్టర్ను లోడ్ చేయడానికి ప్రయత్నిస్తున్నప్పుడు సగం పదం లేదా బైట్ను లోడ్ చేయలేరు. ldh [0] సిస్టమ్ కాల్ seccomp తిరిగి వస్తుంది EINVAL. ఫంక్షన్ లోడ్ చేయబడిన ఫిల్టర్లను తనిఖీ చేస్తుంది seccomp_check_filter() కెర్నలు. (తమాషా ఏమిటంటే, సెకాంప్ ఫంక్షనాలిటీని జోడించిన ఒరిజినల్ కమిట్లో, వారు ఈ ఫంక్షన్కు సూచనలను ఉపయోగించడానికి అనుమతిని జోడించడం మర్చిపోయారు mod (విభజన శేషం) మరియు ఇప్పుడు seccomp BPF ప్రోగ్రామ్ల కోసం అందుబాటులో లేదు, ఇది జోడించినప్పటి నుండి విరిగిపోతుంది ABI.)
సాధారణంగా, సెకాంప్ ప్రోగ్రామ్లను వ్రాయడానికి మరియు చదవడానికి మాకు ఇప్పటికే ప్రతిదీ తెలుసు. సాధారణంగా ప్రోగ్రామ్ లాజిక్ సిస్టమ్ కాల్ల యొక్క తెలుపు లేదా నలుపు జాబితాగా అమర్చబడుతుంది, ఉదాహరణకు ప్రోగ్రామ్
ld [0]
jeq #304, bad
jeq #176, bad
jeq #239, bad
jeq #279, bad
good: ret #0x7fff0000 /* SECCOMP_RET_ALLOW */
bad: ret #0
304, 176, 239, 279 నంబర్ గల నాలుగు సిస్టమ్ కాల్ల బ్లాక్లిస్ట్ని తనిఖీ చేస్తుంది. ఈ సిస్టమ్ కాల్లు ఏమిటి? ప్రోగ్రామ్ ఏ ఆర్కిటెక్చర్ కోసం వ్రాయబడిందో మాకు తెలియదు కాబట్టి మేము ఖచ్చితంగా చెప్పలేము. అందువలన, seccomp రచయితలు ఆఫర్ ఆర్కిటెక్చర్ చెక్తో అన్ని ప్రోగ్రామ్లను ప్రారంభించండి (ప్రస్తుత నిర్మాణం ఫీల్డ్గా సందర్భంలో సూచించబడుతుంది arch ఆకృతి struct seccomp_data) ఆర్కిటెక్చర్ తనిఖీ చేయడంతో, ఉదాహరణ ప్రారంభం ఇలా ఉంటుంది:
ld [4]
jne #0xc000003e, bad_arch ; SCMP_ARCH_X86_64
ఆపై మా సిస్టమ్ కాల్ నంబర్లు నిర్దిష్ట విలువలను పొందుతాయి.
మేము seccomp ఉపయోగించడం కోసం ఫిల్టర్లను వ్రాసి లోడ్ చేస్తాము libseccomp
స్థానిక కోడ్లో లేదా BPF అసెంబ్లీలో ఫిల్టర్లను వ్రాయడం వలన మీరు ఫలితంపై పూర్తి నియంత్రణను కలిగి ఉంటారు, అయితే అదే సమయంలో, కొన్నిసార్లు పోర్టబుల్ మరియు/లేదా చదవగలిగే కోడ్ను కలిగి ఉండటం ఉత్తమం. దీనికి లైబ్రరీ మాకు సహాయం చేస్తుంది libseccomp, ఇది నలుపు లేదా తెలుపు ఫిల్టర్లను వ్రాయడానికి ప్రామాణిక ఇంటర్ఫేస్ను అందిస్తుంది.
ఉదాహరణకు, మునుపు సిస్టమ్ కాల్ల బ్లాక్లిస్ట్ను ఇన్స్టాల్ చేసి, వినియోగదారు ఎంచుకున్న బైనరీ ఫైల్ను అమలు చేసే ప్రోగ్రామ్ను వ్రాద్దాం. పై కథనం (ప్రోగ్రామ్ ఎక్కువ చదవడానికి సరళీకృతం చేయబడింది, పూర్తి సంస్కరణను కనుగొనవచ్చు ఇక్కడ):
#include <seccomp.h>
#include <unistd.h>
#include <err.h>
static int sys_numbers[] = {
__NR_mount,
__NR_umount2,
// ... еще 40 системных вызовов ...
__NR_vmsplice,
__NR_perf_event_open,
};
int main(int argc, char **argv)
{
scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_ALLOW);
for (size_t i = 0; i < sizeof(sys_numbers)/sizeof(sys_numbers[0]); i++)
seccomp_rule_add(ctx, SCMP_ACT_TRAP, sys_numbers[i], 0);
seccomp_load(ctx);
execvp(argv[1], &argv[1]);
err(1, "execlp: %s", argv[1]);
}
మొదట మనం శ్రేణిని నిర్వచించాము sys_numbers బ్లాక్ చేయడానికి 40+ సిస్టమ్ కాల్ నంబర్లు. అప్పుడు, సందర్భాన్ని ప్రారంభించండి ctx మరియు మేము ఏమి అనుమతించాలనుకుంటున్నామో లైబ్రరీకి చెప్పండి (SCMP_ACT_ALLOW) డిఫాల్ట్గా అన్ని సిస్టమ్ కాల్లు (నిరోధిత జాబితాలను రూపొందించడం సులభం). ఆపై, మేము బ్లాక్లిస్ట్ నుండి అన్ని సిస్టమ్ కాల్లను ఒక్కొక్కటిగా జోడిస్తాము. జాబితా నుండి సిస్టమ్ కాల్కు ప్రతిస్పందనగా, మేము అభ్యర్థిస్తున్నాము SCMP_ACT_TRAP, ఈ సందర్భంలో seccomp ప్రక్రియకు సంకేతాన్ని పంపుతుంది SIGSYS ఏ సిస్టమ్ కాల్ నిబంధనలను ఉల్లంఘించిందో వివరణతో. చివరగా, మేము ప్రోగ్రామ్ను ఉపయోగించి కెర్నల్లోకి లోడ్ చేస్తాము seccomp_load, ఇది ప్రోగ్రామ్ను కంపైల్ చేస్తుంది మరియు సిస్టమ్ కాల్ని ఉపయోగించి ప్రాసెస్కు జోడించబడుతుంది seccomp(2).
విజయవంతమైన సంకలనం కోసం, ప్రోగ్రామ్ తప్పనిసరిగా లైబ్రరీతో లింక్ చేయబడాలి libseccompఉదాహరణకు:
cc -std=c17 -Wall -Wextra -c -o seccomp_lib.o seccomp_lib.c
cc -o seccomp_lib seccomp_lib.o -lseccomp
విజయవంతమైన ప్రయోగానికి ఉదాహరణ:
$ ./seccomp_lib echo ok
ok
బ్లాక్ చేయబడిన సిస్టమ్ కాల్కి ఉదాహరణ:
$ sudo ./seccomp_lib mount -t bpf bpf /tmp
Bad system call
మేము ఉపయోగిస్తాము straceవివరాల కోసం:
$ sudo strace -e seccomp ./seccomp_lib mount -t bpf bpf /tmp
seccomp(SECCOMP_SET_MODE_FILTER, 0, {len=50, filter=0x55d8e78428e0}) = 0
--- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0xboobdeadbeef, si_syscall=__NR_mount, si_arch=AUDIT_ARCH_X86_64} ---
+++ killed by SIGSYS (core dumped) +++
Bad system call
చట్టవిరుద్ధమైన సిస్టమ్ కాల్ని ఉపయోగించడం వల్ల ప్రోగ్రామ్ నిలిపివేయబడిందని మనం ఎలా తెలుసుకోవాలి mount(2).
కాబట్టి, మేము లైబ్రరీని ఉపయోగించి ఫిల్టర్ని వ్రాసాము libseccomp, నాన్-ట్రివియల్ కోడ్ని నాలుగు లైన్లలో అమర్చడం. పై ఉదాహరణలో, పెద్ద సంఖ్యలో సిస్టమ్ కాల్లు ఉంటే, చెక్ అనేది కేవలం పోలికల జాబితా అయినందున, అమలు సమయం గమనించదగ్గ విధంగా తగ్గించబడుతుంది. ఆప్టిమైజేషన్ కోసం, libseccomp ఇటీవల కలిగి ఉంది పాచ్ చేర్చబడింది, ఇది ఫిల్టర్ అట్రిబ్యూట్కు మద్దతును జోడిస్తుంది SCMP_FLTATR_CTL_OPTIMIZE. ఈ లక్షణాన్ని 2కి సెట్ చేయడం వలన ఫిల్టర్ బైనరీ శోధన ప్రోగ్రామ్గా మారుతుంది.
మీరు బైనరీ శోధన ఫిల్టర్లు ఎలా పని చేస్తాయో చూడాలనుకుంటే, ఒకసారి చూడండి సాధారణ స్క్రిప్ట్, సిస్టమ్ కాల్ నంబర్లను డయల్ చేయడం ద్వారా BPF అసెంబ్లర్లో ఇటువంటి ప్రోగ్రామ్లను ఉత్పత్తి చేస్తుంది, ఉదాహరణకు:
$ echo 1 3 6 8 13 | ./generate_bin_search_bpf.py
ld [0]
jeq #6, bad
jgt #6, check8
jeq #1, bad
jeq #3, bad
ret #0x7fff0000
check8:
jeq #8, bad
jeq #13, bad
ret #0x7fff0000
bad: ret #0
BPF ప్రోగ్రామ్లు ఇండెంటేషన్ జంప్లను చేయలేనందున, ఏదైనా వేగంగా వ్రాయడం అసాధ్యం (ఉదాహరణకు, మేము చేయలేము, jmp A లేదా jmp [label+X]) అందువలన అన్ని పరివర్తనాలు స్థిరంగా ఉంటాయి.
సెకాంప్ మరియు స్ట్రేస్
ప్రయోజనం అందరికీ తెలుసు strace Linuxలో ప్రక్రియల ప్రవర్తనను అధ్యయనం చేయడానికి ఒక అనివార్య సాధనం. అయితే, చాలా మంది గురించి కూడా విన్నారు పనితీరు సమస్యలు ఈ యుటిలిటీని ఉపయోగిస్తున్నప్పుడు. వాస్తవం ఏమిటంటే strace ఉపయోగించి అమలు ptrace(2), మరియు ఈ మెకానిజంలో మనం ఏ సిస్టమ్ కాల్ల సెట్లో ప్రాసెస్ను ఆపాలి అని పేర్కొనలేము, అంటే, ఉదాహరణకు, ఆదేశాలు
$ time strace du /usr/share/ >/dev/null 2>&1
real 0m3.081s
user 0m0.531s
sys 0m2.073s
и
$ time strace -e open du /usr/share/ >/dev/null 2>&1
real 0m2.404s
user 0m0.193s
sys 0m1.800s
దాదాపు అదే సమయంలో ప్రాసెస్ చేయబడతాయి, అయితే రెండవ సందర్భంలో మేము ఒకే సిస్టమ్ కాల్ని మాత్రమే గుర్తించాలనుకుంటున్నాము.
కొత్త ఎంపిక --seccomp-bpf, జోడించబడింది strace సంస్కరణ 5.3, ప్రక్రియను అనేక సార్లు వేగవంతం చేయడానికి మిమ్మల్ని అనుమతిస్తుంది మరియు ఒక సిస్టమ్ కాల్ ట్రేస్ కింద ప్రారంభ సమయం ఇప్పటికే సాధారణ ప్రారంభ సమయానికి పోల్చవచ్చు:
$ time strace --seccomp-bpf -e open du /usr/share/ >/dev/null 2>&1
real 0m0.148s
user 0m0.017s
sys 0m0.131s
$ time du /usr/share/ >/dev/null 2>&1
real 0m0.140s
user 0m0.024s
sys 0m0.116s
(ఇక్కడ, వాస్తవానికి, మేము ఈ కమాండ్ యొక్క ప్రధాన సిస్టమ్ కాల్ని గుర్తించడం లేదు అనే చిన్న మోసం ఉంది. మనం ట్రేస్ చేస్తుంటే, ఉదాహరణకు, newfsstat, అప్పుడు strace లేకుండా గట్టిగా బ్రేక్ చేస్తుంది --seccomp-bpf.)
ఈ ఎంపిక ఎలా పని చేస్తుంది? ఆమె లేకుండా strace ప్రక్రియకు కనెక్ట్ చేస్తుంది మరియు దానిని ఉపయోగించడం ప్రారంభిస్తుంది PTRACE_SYSCALL. నిర్వహించబడే ప్రక్రియ (ఏదైనా) సిస్టమ్ కాల్ని జారీ చేసినప్పుడు, నియంత్రణ దీనికి బదిలీ చేయబడుతుంది strace, ఇది సిస్టమ్ కాల్ యొక్క ఆర్గ్యుమెంట్లను చూస్తుంది మరియు దానితో రన్ అవుతుంది PTRACE_SYSCALL. కొంత సమయం తరువాత, ప్రక్రియ సిస్టమ్ కాల్ను పూర్తి చేస్తుంది మరియు దాని నుండి నిష్క్రమించినప్పుడు, నియంత్రణ మళ్లీ బదిలీ చేయబడుతుంది strace, ఇది రిటర్న్ విలువలను చూస్తుంది మరియు ఉపయోగించి ప్రక్రియను ప్రారంభిస్తుంది PTRACE_SYSCALL, మరియు మొదలైనవి.
అయితే, సెకాంప్తో, ఈ ప్రక్రియను మనం కోరుకున్నట్లుగానే ఆప్టిమైజ్ చేయవచ్చు. అవి, మనం సిస్టమ్ కాల్ని మాత్రమే చూడాలనుకుంటే X, అప్పుడు మనం దాని కోసం BPF ఫిల్టర్ని వ్రాయవచ్చు X విలువను అందిస్తుంది SECCOMP_RET_TRACE, మరియు మాకు ఆసక్తి లేని కాల్ల కోసం - SECCOMP_RET_ALLOW:
ld [0]
jneq #X, ignore
trace: ret #0x7ff00000
ignore: ret #0x7fff0000
ఈ సందర్భంలో strace మొదటగా ప్రక్రియను ప్రారంభిస్తుంది PTRACE_CONT, సిస్టమ్ కాల్ కాకపోతే ప్రతి సిస్టమ్ కాల్ కోసం మా ఫిల్టర్ ప్రాసెస్ చేయబడుతుంది X, అప్పుడు ప్రక్రియ అమలు కొనసాగుతుంది, కానీ ఇలా ఉంటే X, అప్పుడు seccomp నియంత్రణను బదిలీ చేస్తుంది straceఇది వాదనలను పరిశీలించి, ప్రక్రియను ప్రారంభిస్తుంది PTRACE_SYSCALL (సిస్టమ్ కాల్ నుండి నిష్క్రమించేటప్పుడు ప్రోగ్రామ్ను అమలు చేయగల సామర్థ్యం seccompకి లేదు కాబట్టి). సిస్టమ్ కాల్ తిరిగి వచ్చినప్పుడు, strace ఉపయోగించి ప్రక్రియను పునఃప్రారంభిస్తుంది PTRACE_CONT మరియు seccomp నుండి కొత్త సందేశాల కోసం వేచి ఉంటుంది.
ఎంపికను ఉపయోగిస్తున్నప్పుడు --seccomp-bpf రెండు పరిమితులు ఉన్నాయి. ముందుగా, ఇప్పటికే ఉన్న ప్రక్రియలో చేరడం సాధ్యం కాదు (ఎంపిక -p కార్యక్రమాలు strace), ఎందుకంటే దీనికి seccomp మద్దతు లేదు. రెండవది, అవకాశం లేదు కాదు పిల్లల ప్రక్రియలను చూడండి, ఎందుకంటే seccomp ఫిల్టర్లు దీన్ని డిసేబుల్ చేసే సామర్థ్యం లేకుండానే అన్ని చైల్డ్ ప్రాసెస్ల ద్వారా వారసత్వంగా పొందబడతాయి.
ఎలా ఖచ్చితంగా అనే దానిపై కొంచెం వివరంగా strace తో పనిచేస్తుంది seccomp నుండి కనుగొనవచ్చు ఇటీవలి నివేదిక. మాకు, అత్యంత ఆసక్తికరమైన విషయం ఏమిటంటే, seccomp ద్వారా ప్రాతినిధ్యం వహించే క్లాసిక్ BPF నేటికీ ఉపయోగించబడుతోంది.
xt_bpf
ఇప్పుడు నెట్వర్క్ల ప్రపంచానికి తిరిగి వెళ్దాం.
నేపథ్యం: చాలా కాలం క్రితం, 2007లో, కోర్ ఉంది జోడించబడింది మాడ్యూల్ xt_u32 నెట్ఫిల్టర్ కోసం. ఇది మరింత పురాతన ట్రాఫిక్ వర్గీకరణతో సారూప్యతతో వ్రాయబడింది cls_u32 మరియు కింది సాధారణ కార్యకలాపాలను ఉపయోగించి iptables కోసం ఏకపక్ష బైనరీ నియమాలను వ్రాయడానికి మిమ్మల్ని అనుమతించింది: ఒక ప్యాకేజీ నుండి 32 బిట్లను లోడ్ చేయండి మరియు వాటిపై అంకగణిత కార్యకలాపాల సమితిని అమలు చేయండి. ఉదాహరణకి,
IP హెడర్ యొక్క 32 బిట్లను పాడింగ్ 6 నుండి లోడ్ చేస్తుంది మరియు వాటికి మాస్క్ని వర్తింపజేస్తుంది 0xFF (తక్కువ బైట్ తీసుకోండి). ఈ ఫీల్డ్ protocol IP హెడర్ మరియు మేము దానిని 1 (ICMP)తో పోల్చాము. మీరు ఒక నియమంలో అనేక తనిఖీలను కలపవచ్చు మరియు మీరు ఆపరేటర్ను కూడా అమలు చేయవచ్చు @ — X బైట్లను కుడివైపుకు తరలించండి. ఉదాహరణకు, నియమం
TCP సీక్వెన్స్ సంఖ్య సమానంగా లేకుంటే తనిఖీ చేస్తుంది 0x29. అటువంటి నియమాలను చేతితో రాయడం చాలా సౌకర్యవంతంగా లేదని ఇప్పటికే స్పష్టంగా ఉన్నందున నేను మరింత వివరాల్లోకి వెళ్లను. వ్యాసంలో BPF - మర్చిపోయిన బైట్కోడ్, ఉపయోగం మరియు రూల్ జనరేషన్ యొక్క ఉదాహరణలతో అనేక లింక్లు ఉన్నాయి xt_u32. ఈ వ్యాసం చివరిలో ఉన్న లింక్లను కూడా చూడండి.
మాడ్యూల్కు బదులుగా 2013 నుండి మాడ్యూల్ xt_u32 మీరు BPF ఆధారిత మాడ్యూల్ని ఉపయోగించవచ్చు xt_bpf. ఇంతవరకు చదివిన ఎవరైనా దాని ఆపరేషన్ సూత్రం గురించి ఇప్పటికే స్పష్టంగా ఉండాలి: BPF బైట్కోడ్ను iptables నియమాల వలె అమలు చేయండి. మీరు కొత్త నియమాన్ని సృష్టించవచ్చు, ఉదాహరణకు, ఇలా:
iptables -A INPUT -m bpf --bytecode <байткод> -j LOG
ఇక్కడ <байткод> - ఇది అసెంబ్లర్ అవుట్పుట్ ఫార్మాట్లోని కోడ్ bpf_asm డిఫాల్ట్గా, ఉదాహరణకు,
ఈ ఉదాహరణలో మేము అన్ని UDP ప్యాకెట్లను ఫిల్టర్ చేస్తున్నాము. మాడ్యూల్లో BPF ప్రోగ్రామ్ కోసం సందర్భం xt_bpf, వాస్తవానికి, iptables విషయంలో, IPv4 హెడర్ ప్రారంభానికి ప్యాకెట్ డేటాను సూచిస్తుంది. BPF ప్రోగ్రామ్ నుండి తిరిగి విలువ బూలియన్పేరు false అంటే ప్యాకెట్ సరిపోలలేదు.
మాడ్యూల్ అని స్పష్టమైంది xt_bpf ఎగువ ఉదాహరణ కంటే చాలా క్లిష్టమైన ఫిల్టర్లకు మద్దతు ఇస్తుంది. Cloudfare నుండి నిజమైన ఉదాహరణలను చూద్దాం. ఇటీవలి వరకు వారు మాడ్యూల్ను ఉపయోగించారు xt_bpf DDoS దాడుల నుండి రక్షించడానికి. వ్యాసంలో BPF సాధనాలను పరిచయం చేస్తున్నాము వారు BPF ఫిల్టర్లను ఎలా ఉత్పత్తి చేస్తారో (మరియు ఎందుకు) వివరిస్తారు మరియు అటువంటి ఫిల్టర్లను సృష్టించడం కోసం యుటిలిటీల సమితికి లింక్లను ప్రచురించారు. ఉదాహరణకు, యుటిలిటీని ఉపయోగించడం bpfgen మీరు పేరు కోసం DNS ప్రశ్నకు సరిపోలే BPF ప్రోగ్రామ్ను సృష్టించవచ్చు habr.com:
$ ./bpfgen --assembly dns -- habr.com
ldx 4*([0]&0xf)
ld #20
add x
tax
lb_0:
ld [x + 0]
jneq #0x04686162, lb_1
ld [x + 4]
jneq #0x7203636f, lb_1
ldh [x + 8]
jneq #0x6d00, lb_1
ret #65535
lb_1:
ret #0
ప్రోగ్రామ్లో మేము మొదట రిజిస్టర్లోకి లోడ్ చేస్తాము X లైన్ చిరునామా ప్రారంభం x04habrx03comx00 UDP డేటాగ్రామ్ లోపల మరియు అభ్యర్థనను తనిఖీ చేయండి: 0x04686162 <-> "x04hab" మరియు అందువలన న.
కొద్దిసేపటి తర్వాత, క్లౌడ్ఫేర్ p0f -> BPF కంపైలర్ కోడ్ను ప్రచురించింది. వ్యాసంలో p0f BPF కంపైలర్ని పరిచయం చేస్తున్నాము వారు p0f అంటే ఏమిటి మరియు p0f సంతకాలను BPFకి ఎలా మార్చాలి అనే దాని గురించి మాట్లాడతారు:
ప్రస్తుతం క్లౌడ్ఫేర్ని ఉపయోగించడం లేదు xt_bpf, వారు XDPకి మారినందున - BPF యొక్క కొత్త వెర్షన్ని ఉపయోగించడం కోసం ఎంపికలలో ఒకటి, చూడండి. L4Drop: XDP DDoS ఉపశమనాలు.
cls_bpf
కెర్నల్లో క్లాసిక్ BPFని ఉపయోగించడం యొక్క చివరి ఉదాహరణ వర్గీకరణ cls_bpf Linuxలో ట్రాఫిక్ నియంత్రణ సబ్సిస్టమ్ కోసం, 2013 చివరిలో Linuxకి జోడించబడింది మరియు సంభావితంగా పురాతనమైనదిగా భర్తీ చేయబడింది cls_u32.
అయితే, మేము ఇప్పుడు పనిని వివరించము cls_bpf, క్లాసిక్ BPF గురించి జ్ఞానం యొక్క దృక్కోణం నుండి ఇది మాకు ఏమీ ఇవ్వదు - మేము ఇప్పటికే అన్ని కార్యాచరణలతో సుపరిచితం. అదనంగా, విస్తరించిన BPF గురించి మాట్లాడే తదుపరి కథనాలలో, మేము ఈ వర్గీకరణను ఒకటి కంటే ఎక్కువసార్లు కలుస్తాము.
క్లాసిక్ BPF cని ఉపయోగించడం గురించి మాట్లాడకపోవడానికి మరొక కారణం cls_bpf సమస్య ఏమిటంటే, విస్తరించిన BPFతో పోలిస్తే, ఈ సందర్భంలో వర్తించే పరిధి సమూలంగా కుదించబడింది: క్లాసికల్ ప్రోగ్రామ్లు ప్యాకేజీల కంటెంట్లను మార్చలేవు మరియు కాల్ల మధ్య స్థితిని సేవ్ చేయలేవు.
కాబట్టి ఇది క్లాసిక్ BPFకి వీడ్కోలు చెప్పే సమయం మరియు భవిష్యత్తును చూడటం.
క్లాసిక్ BPFకి వీడ్కోలు
తొంభైల ప్రారంభంలో అభివృద్ధి చెందిన BPF సాంకేతికత పావు శతాబ్దం పాటు విజయవంతంగా ఎలా జీవించిందో మరియు చివరి వరకు కొత్త అనువర్తనాలను ఎలా కనుగొన్నదో మేము చూశాము. ఏది ఏమైనప్పటికీ, స్టాక్ మెషీన్ల నుండి RISCకి మారడం మాదిరిగానే, ఇది క్లాసిక్ BPF అభివృద్ధికి ప్రేరణగా పనిచేసింది, 32లలో 64-బిట్ నుండి XNUMX-బిట్ మెషీన్లకు పరివర్తన జరిగింది మరియు క్లాసిక్ BPF వాడుకలో లేదు. అదనంగా, క్లాసిక్ BPF యొక్క సామర్థ్యాలు చాలా పరిమితం, మరియు పాత ఆర్కిటెక్చర్తో పాటు - BPF ప్రోగ్రామ్లకు కాల్ల మధ్య స్థితిని సేవ్ చేసే సామర్థ్యం మాకు లేదు, ప్రత్యక్ష వినియోగదారు పరస్పర చర్యకు అవకాశం లేదు, పరస్పర చర్య చేసే అవకాశం లేదు కెర్నల్తో, పరిమిత సంఖ్యలో స్ట్రక్చర్ ఫీల్డ్లను చదవడం మినహా sk_buff మరియు సరళమైన సహాయక విధులను ప్రారంభించడం, మీరు ప్యాకెట్ల కంటెంట్లను మార్చలేరు మరియు వాటిని దారి మళ్లించలేరు.
వాస్తవానికి, ప్రస్తుతం లైనక్స్లో క్లాసిక్ BPFలో మిగిలి ఉన్నదంతా API ఇంటర్ఫేస్, మరియు కెర్నల్ లోపల అన్ని క్లాసిక్ ప్రోగ్రామ్లు, సాకెట్ ఫిల్టర్లు లేదా సెకాంప్ ఫిల్టర్లు అయినా, స్వయంచాలకంగా కొత్త ఫార్మాట్, ఎక్స్టెండెడ్ BPFలోకి అనువదించబడతాయి. (తరువాతి వ్యాసంలో ఇది ఎలా జరుగుతుందనే దాని గురించి మేము మాట్లాడుతాము.)
2013లో అలెక్సీ స్టార్వోయిటోవ్ BPF అప్డేట్ స్కీమ్ను ప్రతిపాదించినప్పుడు కొత్త ఆర్కిటెక్చర్కు మార్పు ప్రారంభమైంది. 2014లో సంబంధిత పాచెస్ కనిపించడం మొదలైంది కోర్ లో. నేను అర్థం చేసుకున్నంతవరకు, ప్రారంభ ప్రణాళిక 64-బిట్ మెషీన్లపై మరింత సమర్థవంతంగా అమలు చేయడానికి ఆర్కిటెక్చర్ మరియు JIT కంపైలర్ను ఆప్టిమైజ్ చేయడం మాత్రమే, కానీ బదులుగా ఈ ఆప్టిమైజేషన్లు Linux డెవలప్మెంట్లో కొత్త అధ్యాయానికి నాంది పలికాయి.
ఈ శ్రేణిలోని మరిన్ని కథనాలు కొత్త సాంకేతికత యొక్క నిర్మాణం మరియు అనువర్తనాలను కవర్ చేస్తుంది, దీనిని మొదట్లో అంతర్గత BPF అని పిలుస్తారు, తర్వాత పొడిగించిన BPF మరియు ఇప్పుడు కేవలం BPF అని పిలుస్తారు.
సూచనలు
స్టీవెన్ మెక్కన్ మరియు వాన్ జాకబ్సన్, "ది BSD ప్యాకెట్ ఫిల్టర్: ఎ న్యూ ఆర్కిటెక్చర్ ఫర్ యూజర్-లెవల్ ప్యాకెట్ క్యాప్చర్", https://www.tcpdump.org/papers/bpf-usenix93.pdf
స్టీవెన్ మెక్కన్, "libpcap: ప్యాకెట్ క్యాప్చర్ కోసం ఒక ఆర్కిటెక్చర్ మరియు ఆప్టిమైజేషన్ మెథడాలజీ", https://sharkfestus.wireshark.org/sharkfest.11/presentations/McCanne-Sharkfest'11_Keynote_Address.pdf