yannstatic/static/2019/12/25/freebox-IPv6-delegation.html

2304 lines
202 KiB
HTML
Raw Permalink Normal View History

2024-10-31 20:18:37 +01:00
<!DOCTYPE html><html lang="fr">
<head><meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"><title>freebox--IPv6-delegation - YannStatic</title>
<meta name="description" content="IPV6 Freebox">
<link rel="canonical" href="https://static.rnmkcy.eu/2019/12/25/freebox-IPv6-delegation.html"><link rel="alternate" type="application/rss+xml" title="YannStatic" href="/feed.xml">
<!-- - include head/favicon.html - -->
<link rel="shortcut icon" type="image/png" href="/assets/favicon/favicon.png"><link rel="stylesheet" href="/assets/css/main.css"><link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.13/css/all.css" ><!-- start custom head snippets --><link rel="stylesheet" href="/assets/css/expand.css">
<!-- end custom head snippets --><script>(function() {
window.isArray = function(val) {
return Object.prototype.toString.call(val) === '[object Array]';
};
window.isString = function(val) {
return typeof val === 'string';
};
window.hasEvent = function(event) {
return 'on'.concat(event) in window.document;
};
window.isOverallScroller = function(node) {
return node === document.documentElement || node === document.body || node === window;
};
window.isFormElement = function(node) {
var tagName = node.tagName;
return tagName === 'INPUT' || tagName === 'SELECT' || tagName === 'TEXTAREA';
};
window.pageLoad = (function () {
var loaded = false, cbs = [];
window.addEventListener('load', function () {
var i;
loaded = true;
if (cbs.length > 0) {
for (i = 0; i < cbs.length; i++) {
cbs[i]();
}
}
});
return {
then: function(cb) {
cb && (loaded ? cb() : (cbs.push(cb)));
}
};
})();
})();
(function() {
window.throttle = function(func, wait) {
var args, result, thisArg, timeoutId, lastCalled = 0;
function trailingCall() {
lastCalled = new Date;
timeoutId = null;
result = func.apply(thisArg, args);
}
return function() {
var now = new Date,
remaining = wait - (now - lastCalled);
args = arguments;
thisArg = this;
if (remaining <= 0) {
clearTimeout(timeoutId);
timeoutId = null;
lastCalled = now;
result = func.apply(thisArg, args);
} else if (!timeoutId) {
timeoutId = setTimeout(trailingCall, remaining);
}
return result;
};
};
})();
(function() {
var Set = (function() {
var add = function(item) {
var i, data = this._data;
for (i = 0; i < data.length; i++) {
if (data[i] === item) {
return;
}
}
this.size ++;
data.push(item);
return data;
};
var Set = function(data) {
this.size = 0;
this._data = [];
var i;
if (data.length > 0) {
for (i = 0; i < data.length; i++) {
add.call(this, data[i]);
}
}
};
Set.prototype.add = add;
Set.prototype.get = function(index) { return this._data[index]; };
Set.prototype.has = function(item) {
var i, data = this._data;
for (i = 0; i < data.length; i++) {
if (this.get(i) === item) {
return true;
}
}
return false;
};
Set.prototype.is = function(map) {
if (map._data.length !== this._data.length) { return false; }
var i, j, flag, tData = this._data, mData = map._data;
for (i = 0; i < tData.length; i++) {
for (flag = false, j = 0; j < mData.length; j++) {
if (tData[i] === mData[j]) {
flag = true;
break;
}
}
if (!flag) { return false; }
}
return true;
};
Set.prototype.values = function() {
return this._data;
};
return Set;
})();
window.Lazyload = (function(doc) {
var queue = {js: [], css: []}, sources = {js: {}, css: {}}, context = this;
var createNode = function(name, attrs) {
var node = doc.createElement(name), attr;
for (attr in attrs) {
if (attrs.hasOwnProperty(attr)) {
node.setAttribute(attr, attrs[attr]);
}
}
return node;
};
var end = function(type, url) {
var s, q, qi, cbs, i, j, cur, val, flag;
if (type === 'js' || type ==='css') {
s = sources[type], q = queue[type];
s[url] = true;
for (i = 0; i < q.length; i++) {
cur = q[i];
if (cur.urls.has(url)) {
qi = cur, val = qi.urls.values();
qi && (cbs = qi.callbacks);
for (flag = true, j = 0; j < val.length; j++) {
cur = val[j];
if (!s[cur]) {
flag = false;
}
}
if (flag && cbs && cbs.length > 0) {
for (j = 0; j < cbs.length; j++) {
cbs[j].call(context);
}
qi.load = true;
}
}
}
}
};
var load = function(type, urls, callback) {
var s, q, qi, node, i, cur,
_urls = typeof urls === 'string' ? new Set([urls]) : new Set(urls), val, url;
if (type === 'js' || type ==='css') {
s = sources[type], q = queue[type];
for (i = 0; i < q.length; i++) {
cur = q[i];
if (_urls.is(cur.urls)) {
qi = cur;
break;
}
}
val = _urls.values();
if (qi) {
callback && (qi.load || qi.callbacks.push(callback));
callback && (qi.load && callback());
} else {
q.push({
urls: _urls,
callbacks: callback ? [callback] : [],
load: false
});
for (i = 0; i < val.length; i++) {
node = null, url = val[i];
if (s[url] === undefined) {
(type === 'js' ) && (node = createNode('script', { src: url }));
(type === 'css') && (node = createNode('link', { rel: 'stylesheet', href: url }));
if (node) {
node.onload = (function(type, url) {
return function() {
end(type, url);
};
})(type, url);
(doc.head || doc.body).appendChild(node);
s[url] = false;
}
}
}
}
}
};
return {
js: function(url, callback) {
load('js', url, callback);
},
css: function(url, callback) {
load('css', url, callback);
}
};
})(this.document);
})();
</script><script>
(function() {
var TEXT_VARIABLES = {
version: '2.2.6',
sources: {
font_awesome: 'https://use.fontawesome.com/releases/v5.0.13/css/all.css',
jquery: '/assets/js/jquery.min.js',
leancloud_js_sdk: '//cdn.jsdelivr.net/npm/leancloud-storage@3.13.2/dist/av-min.js',
chart: 'https://cdn.bootcss.com/Chart.js/2.7.2/Chart.bundle.min.js',
gitalk: {
js: 'https://cdn.bootcss.com/gitalk/1.2.2/gitalk.min.js',
css: 'https://cdn.bootcss.com/gitalk/1.2.2/gitalk.min.css'
},
valine: 'https://unpkg.com/valine/dist/Valine.min.js'
},
site: {
toc: {
selectors: 'h1,h2,h3'
}
},
paths: {
search_js: '/assets/search.js'
}
};
window.TEXT_VARIABLES = TEXT_VARIABLES;
})();
</script>
</head>
<body>
<div class="root" data-is-touch="false">
<div class="layout--page js-page-root"><!----><div class="page__main js-page-main page__viewport hide-footer has-aside has-aside cell cell--auto">
<div class="page__main-inner"><div class="page__header d-print-none"><header class="header"><div class="main">
<div class="header__title">
<div class="header__brand"><svg id="svg" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="400" height="478.9473684210526" viewBox="0, 0, 400,478.9473684210526"><g id="svgg"><path id="path0" d="M308.400 56.805 C 306.970 56.966,303.280 57.385,300.200 57.738 C 290.906 58.803,278.299 59.676,269.200 59.887 L 260.600 60.085 259.400 61.171 C 258.010 62.428,256.198 63.600,255.645 63.600 C 255.070 63.600,252.887 65.897,252.598 66.806 C 252.460 67.243,252.206 67.600,252.034 67.600 C 251.397 67.600,247.206 71.509,247.202 72.107 C 247.201 72.275,246.390 73.190,245.400 74.138 C 243.961 75.517,243.598 76.137,243.592 77.231 C 243.579 79.293,241.785 83.966,240.470 85.364 C 239.176 86.740,238.522 88.365,237.991 91.521 C 237.631 93.665,236.114 97.200,235.554 97.200 C 234.938 97.200,232.737 102.354,232.450 104.472 C 232.158 106.625,230.879 109.226,229.535 110.400 C 228.933 110.926,228.171 113.162,226.434 119.500 C 226.178 120.435,225.795 121.200,225.584 121.200 C 225.373 121.200,225.200 121.476,225.200 121.813 C 225.200 122.149,224.885 122.541,224.500 122.683 C 223.606 123.013,223.214 123.593,223.204 124.600 C 223.183 126.555,220.763 132.911,219.410 134.562 C 218.443 135.742,217.876 136.956,217.599 138.440 C 217.041 141.424,215.177 146.434,214.532 146.681 C 214.240 146.794,214.000 147.055,214.000 147.261 C 214.000 147.467,213.550 148.086,213.000 148.636 C 212.450 149.186,212.000 149.893,212.000 150.208 C 212.000 151.386,208.441 154.450,207.597 153.998 C 206.319 153.315,204.913 150.379,204.633 147.811 C 204.365 145.357,202.848 142.147,201.759 141.729 C 200.967 141.425,199.200 137.451,199.200 135.974 C 199.200 134.629,198.435 133.224,196.660 131.311 C 195.363 129.913,194.572 128.123,193.870 125.000 C 193.623 123.900,193.236 122.793,193.010 122.540 C 190.863 120.133,190.147 118.880,188.978 115.481 C 188.100 112.928,187.151 111.003,186.254 109.955 C 185.358 108.908,184.518 107.204,183.847 105.073 C 183.280 103.273,182.497 101.329,182.108 100.753 C 181.719 100.177,180.904 98.997,180.298 98.131 C 179.693 97.265,178.939 95.576,178.624 94.378 C 178.041 92.159,177.125 90.326,175.023 87.168 C 174.375 86.196,173.619 84.539,173.342 83.486 C 172.800 81.429,171.529 79.567,170.131 78.785 C 169.654 78.517,168.697 77.511,168.006 76.549 C 167.316 75.587,166.594 74.800,166.402 74.800 C 166.210 74.800,164.869 73.633,163.421 72.206 C 160.103 68.936,161.107 69.109,146.550 69.301 C 133.437 69.474,128.581 70.162,126.618 72.124 C 126.248 72.495,125.462 72.904,124.872 73.033 C 124.282 73.163,123.088 73.536,122.219 73.863 C 121.349 74.191,119.028 74.638,117.061 74.858 C 113.514 75.254,109.970 76.350,108.782 77.419 C 107.652 78.436,100.146 80.400,97.388 80.400 C 95.775 80.400,93.167 81.360,91.200 82.679 C 90.430 83.195,89.113 83.804,88.274 84.031 C 85.875 84.681,78.799 90.910,74.400 96.243 L 73.400 97.456 73.455 106.028 C 73.526 117.055,74.527 121.238,77.820 124.263 C 78.919 125.273,80.400 127.902,80.400 128.842 C 80.400 129.202,81.075 130.256,81.900 131.186 C 83.563 133.059,85.497 136.346,86.039 138.216 C 86.233 138.886,87.203 140.207,88.196 141.153 C 89.188 142.098,90.000 143.104,90.000 143.388 C 90.000 144.337,92.129 148.594,92.869 149.123 C 93.271 149.410,93.600 149.831,93.600 150.059 C 93.600 150.286,93.932 150.771,94.337 151.136 C 94.743 151.501,95.598 153.004,96.237 154.475 C 96.877 155.947,97.760 157.351,98.200 157.596 C 98.640 157.841,99.900 159.943,101.000 162.267 C 102.207 164.817,103.327 166.644,103.825 166.876 C 104.278 167.087,105.065 168.101,105.573 169.130 C 107.658 173.348,108.097 174.093,110.006 176.647 C 111.103 178.114,112.000 179.725,112.000 180.227 C 112.000 181.048,113.425 183.163,114.678 184.200 C 115.295 184.711,117.396 188.733,117.720 190.022 C 117.855 190.562,118.603 191.633,119.381 192.402 C 120.160 193.171,121.496 195.258,122.351 197.039 C 123.206 198.820,124.167 200.378,124.487 200.501 C 124.807 200.624,125.953 202.496,127.034 204.662 C 128.114 206.828,129.676 209.299,130.505 210.153 C 131.333 211.007,132.124 212.177,132.262 212.753 C 132.618 214.239,134.291 217.048,136.288 219.5
" href="/">YannStatic</a></div><!--<button class="button button--secondary button--circle search-button js-search-toggle"><i class="fas fa-search"></i></button>--><!-- <li><button class="button button--secondary button--circle search-button js-search-toggle"><i class="fas fa-search"></i></button></li> -->
<!-- Champ de recherche -->
<div id="searchbox" class="search search--dark" style="visibility: visible">
<div class="main">
<div class="search__header"></div>
<div class="search-bar">
<div class="search-box js-search-box">
<div class="search-box__icon-search"><i class="fas fa-search"></i></div>
<input id="search-input" type="text" />
<!-- <div class="search-box__icon-clear js-icon-clear">
<a><i class="fas fa-times"></i></a>
</div> -->
</div>
</div>
</div>
</div>
<!-- Script pointing to search-script.js -->
<script>/*!
* Simple-Jekyll-Search
* Copyright 2015-2020, Christian Fei
* Licensed under the MIT License.
*/
(function(){
'use strict'
var _$Templater_7 = {
compile: compile,
setOptions: setOptions
}
const options = {}
options.pattern = /\{(.*?)\}/g
options.template = ''
options.middleware = function () {}
function setOptions (_options) {
options.pattern = _options.pattern || options.pattern
options.template = _options.template || options.template
if (typeof _options.middleware === 'function') {
options.middleware = _options.middleware
}
}
function compile (data) {
return options.template.replace(options.pattern, function (match, prop) {
const value = options.middleware(prop, data[prop], options.template)
if (typeof value !== 'undefined') {
return value
}
return data[prop] || match
})
}
'use strict';
function fuzzysearch (needle, haystack) {
var tlen = haystack.length;
var qlen = needle.length;
if (qlen > tlen) {
return false;
}
if (qlen === tlen) {
return needle === haystack;
}
outer: for (var i = 0, j = 0; i < qlen; i++) {
var nch = needle.charCodeAt(i);
while (j < tlen) {
if (haystack.charCodeAt(j++) === nch) {
continue outer;
}
}
return false;
}
return true;
}
var _$fuzzysearch_1 = fuzzysearch;
'use strict'
/* removed: const _$fuzzysearch_1 = require('fuzzysearch') */;
var _$FuzzySearchStrategy_5 = new FuzzySearchStrategy()
function FuzzySearchStrategy () {
this.matches = function (string, crit) {
return _$fuzzysearch_1(crit.toLowerCase(), string.toLowerCase())
}
}
'use strict'
var _$LiteralSearchStrategy_6 = new LiteralSearchStrategy()
function LiteralSearchStrategy () {
this.matches = function (str, crit) {
if (!str) return false
str = str.trim().toLowerCase()
crit = crit.trim().toLowerCase()
return crit.split(' ').filter(function (word) {
return str.indexOf(word) >= 0
}).length === crit.split(' ').length
}
}
'use strict'
var _$Repository_4 = {
put: put,
clear: clear,
search: search,
setOptions: __setOptions_4
}
/* removed: const _$FuzzySearchStrategy_5 = require('./SearchStrategies/FuzzySearchStrategy') */;
/* removed: const _$LiteralSearchStrategy_6 = require('./SearchStrategies/LiteralSearchStrategy') */;
function NoSort () {
return 0
}
const data = []
let opt = {}
opt.fuzzy = false
opt.limit = 10
opt.searchStrategy = opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6
opt.sort = NoSort
opt.exclude = []
function put (data) {
if (isObject(data)) {
return addObject(data)
}
if (isArray(data)) {
return addArray(data)
}
return undefined
}
function clear () {
data.length = 0
return data
}
function isObject (obj) {
return Boolean(obj) && Object.prototype.toString.call(obj) === '[object Object]'
}
function isArray (obj) {
return Boolean(obj) && Object.prototype.toString.call(obj) === '[object Array]'
}
function addObject (_data) {
data.push(_data)
return data
}
function addArray (_data) {
const added = []
clear()
for (let i = 0, len = _data.length; i < len; i++) {
if (isObject(_data[i])) {
added.push(addObject(_data[i]))
}
}
return added
}
function search (crit) {
if (!crit) {
return []
}
return findMatches(data, crit, opt.searchStrategy, opt).sort(opt.sort)
}
function __setOptions_4 (_opt) {
opt = _opt || {}
opt.fuzzy = _opt.fuzzy || false
opt.limit = _opt.limit || 10
opt.searchStrategy = _opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6
opt.sort = _opt.sort || NoSort
opt.exclude = _opt.exclude || []
}
function findMatches (data, crit, strategy, opt) {
const matches = []
for (let i = 0; i < data.length && matches.length < opt.limit; i++) {
const match = findMatchesInObject(data[i], crit, strategy, opt)
if (match) {
matches.push(match)
}
}
return matches
}
function findMatchesInObject (obj, crit, strategy, opt) {
for (const key in obj) {
if (!isExcluded(obj[key], opt.exclude) && strategy.matches(obj[key], crit)) {
return obj
}
}
}
function isExcluded (term, excludedTerms) {
for (let i = 0, len = excludedTerms.length; i < len; i++) {
const excludedTerm = excludedTerms[i]
if (new RegExp(excludedTerm).test(term)) {
return true
}
}
return false
}
/* globals ActiveXObject:false */
'use strict'
var _$JSONLoader_2 = {
load: load
}
function load (location, callback) {
const xhr = getXHR()
xhr.open('GET', location, true)
xhr.onreadystatechange = createStateChangeListener(xhr, callback)
xhr.send()
}
function createStateChangeListener (xhr, callback) {
return function () {
if (xhr.readyState === 4 && xhr.status === 200) {
try {
callback(null, JSON.parse(xhr.responseText))
} catch (err) {
callback(err, null)
}
}
}
}
function getXHR () {
return window.XMLHttpRequest ? new window.XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP')
}
'use strict'
var _$OptionsValidator_3 = function OptionsValidator (params) {
if (!validateParams(params)) {
throw new Error('-- OptionsValidator: required options missing')
}
if (!(this instanceof OptionsValidator)) {
return new OptionsValidator(params)
}
const requiredOptions = params.required
this.getRequiredOptions = function () {
return requiredOptions
}
this.validate = function (parameters) {
const errors = []
requiredOptions.forEach(function (requiredOptionName) {
if (typeof parameters[requiredOptionName] === 'undefined') {
errors.push(requiredOptionName)
}
})
return errors
}
function validateParams (params) {
if (!params) {
return false
}
return typeof params.required !== 'undefined' && params.required instanceof Array
}
}
'use strict'
var _$utils_9 = {
merge: merge,
isJSON: isJSON
}
function merge (defaultParams, mergeParams) {
const mergedOptions = {}
for (const option in defaultParams) {
mergedOptions[option] = defaultParams[option]
if (typeof mergeParams[option] !== 'undefined') {
mergedOptions[option] = mergeParams[option]
}
}
return mergedOptions
}
function isJSON (json) {
try {
if (json instanceof Object && JSON.parse(JSON.stringify(json))) {
return true
}
return false
} catch (err) {
return false
}
}
var _$src_8 = {};
(function (window) {
'use strict'
let options = {
searchInput: null,
resultsContainer: null,
json: [],
success: Function.prototype,
searchResultTemplate: '<li><a href="{url}" title="{desc}">{title}</a></li>',
templateMiddleware: Function.prototype,
sortMiddleware: function () {
return 0
},
noResultsText: 'No results found',
limit: 10,
fuzzy: false,
debounceTime: null,
exclude: []
}
let debounceTimerHandle
const debounce = function (func, delayMillis) {
if (delayMillis) {
clearTimeout(debounceTimerHandle)
debounceTimerHandle = setTimeout(func, delayMillis)
} else {
func.call()
}
}
const requiredOptions = ['searchInput', 'resultsContainer', 'json']
/* removed: const _$Templater_7 = require('./Templater') */;
/* removed: const _$Repository_4 = require('./Repository') */;
/* removed: const _$JSONLoader_2 = require('./JSONLoader') */;
const optionsValidator = _$OptionsValidator_3({
required: requiredOptions
})
/* removed: const _$utils_9 = require('./utils') */;
window.SimpleJekyllSearch = function (_options) {
const errors = optionsValidator.validate(_options)
if (errors.length > 0) {
throwError('You must specify the following required options: ' + requiredOptions)
}
options = _$utils_9.merge(options, _options)
_$Templater_7.setOptions({
template: options.searchResultTemplate,
middleware: options.templateMiddleware
})
_$Repository_4.setOptions({
fuzzy: options.fuzzy,
limit: options.limit,
sort: options.sortMiddleware,
exclude: options.exclude
})
if (_$utils_9.isJSON(options.json)) {
initWithJSON(options.json)
} else {
initWithURL(options.json)
}
const rv = {
search: search
}
typeof options.success === 'function' && options.success.call(rv)
return rv
}
function initWithJSON (json) {
_$Repository_4.put(json)
registerInput()
}
function initWithURL (url) {
_$JSONLoader_2.load(url, function (err, json) {
if (err) {
throwError('failed to get JSON (' + url + ')')
}
initWithJSON(json)
})
}
function emptyResultsContainer () {
options.resultsContainer.innerHTML = ''
}
function appendToResultsContainer (text) {
options.resultsContainer.innerHTML += text
}
function registerInput () {
options.searchInput.addEventListener('input', function (e) {
if (isWhitelistedKey(e.which)) {
emptyResultsContainer()
debounce(function () { search(e.target.value) }, options.debounceTime)
}
})
}
function search (query) {
if (isValidQuery(query)) {
emptyResultsContainer()
render(_$Repository_4.search(query), query)
}
}
function render (results, query) {
const len = results.length
if (len === 0) {
return appendToResultsContainer(options.noResultsText)
}
for (let i = 0; i < len; i++) {
results[i].query = query
appendToResultsContainer(_$Templater_7.compile(results[i]))
}
}
function isValidQuery (query) {
return query && query.length > 0
}
function isWhitelistedKey (key) {
return [13, 16, 20, 37, 38, 39, 40, 91].indexOf(key) === -1
}
function throwError (message) {
throw new Error('SimpleJekyllSearch --- ' + message)
}
})(window)
}());
</script>
<!-- Configuration -->
<script>
SimpleJekyllSearch({
searchInput: document.getElementById('search-input'),
resultsContainer: document.getElementById('results-container'),
json: '/search.json',
//searchResultTemplate: '<li><a href="https://static.rnmkcy.eu{url}">{date}&nbsp;{title}</a></li>'
searchResultTemplate: '<li><a href="{url}">{date}&nbsp;{title}</a></li>'
})
</script>
<!-- Fin déclaration champ de recherche --></div><nav class="navigation">
<ul><li class="navigation__item"><a href="/archive.html">Etiquettes</a></li><li class="navigation__item"><a href="/htmldoc.html">Documents</a></li><li class="navigation__item"><a href="/liens_ttrss.html">Liens</a></li><li class="navigation__item"><a href="/aide-jekyll-text-theme.html">Aide</a></li></ul>
</nav></div>
</header>
</div><div class="page__content"><div class ="main"><div class="grid grid--reverse">
<div class="col-main cell cell--auto"><!-- start custom main top snippet --><div id="results-container" class="search-result js-search-result"></div><!-- end custom main top snippet -->
<article itemscope itemtype="http://schema.org/Article"><div class="article__header"><header><h1 style="color:Tomato;">freebox--IPv6-delegation</h1></header></div><meta itemprop="headline" content="freebox--IPv6-delegation"><div class="article__info clearfix"><ul class="left-col menu"><li>
2024-11-08 14:10:33 +01:00
<a class="button button--secondary button--pill button--sm" style="color:#00FFFF" href="/archive.html?tag=box">box</a>
2024-10-31 20:18:37 +01:00
</li></ul><ul class="right-col menu"><li>
<i class="far fa-calendar-alt"></i>&nbsp;<span title="Création" style="color:#FF00FF">25&nbsp;déc.&nbsp;&nbsp;2019</span>
<span title="Modification" style="color:#00FF7F">23&nbsp;nov.&nbsp;&nbsp;2018</span></li></ul></div><meta itemprop="datePublished" content="2018-11-23T00:00:00+01:00">
<meta itemprop="keywords" content="box"><div class="js-article-content">
<div class="layout--article"><!-- start custom article top snippet -->
<style>
#myBtn {
display: none;
position: fixed;
bottom: 10px;
right: 10px;
z-index: 99;
font-size: 12px;
font-weight: bold;
border: none;
outline: none;
background-color: white;
color: black;
cursor: pointer;
padding: 5px;
border-radius: 4px;
}
#myBtn:hover {
background-color: #555;
}
</style>
<button onclick="topFunction()" id="myBtn" title="Haut de page">&#8679;</button>
<script>
//Get the button
var mybutton = document.getElementById("myBtn");
// When the user scrolls down 20px from the top of the document, show the button
window.onscroll = function() {scrollFunction()};
function scrollFunction() {
if (document.body.scrollTop > 20 || document.documentElement.scrollTop > 20) {
mybutton.style.display = "block";
} else {
mybutton.style.display = "none";
}
}
// When the user clicks on the button, scroll to the top of the document
function topFunction() {
document.body.scrollTop = 0;
document.documentElement.scrollTop = 0;
}
</script>
<!-- end custom article top snippet -->
<div class="article__content" itemprop="articleBody"><details>
<summary><b>Afficher/cacher Sommaire</b></summary>
<!-- affichage sommaire -->
<div class="toc-aside js-toc-root"></div>
</details><h1 id="ipv6-freebox">IPV6 Freebox</h1>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>De plus en plus de réseaux proposent une adresse en IPv6. Je ne détaillerai pas ce quest IPv6, le pourquoi et la nécessité de le développer...
Obtenir une IPv6 sur sa machine Yunohost
Dans les prérequis, il y a :
sur la Freebox, dans la partie administration, avoir activé lIPv6.
avoir IPv6 dactif sur lOS sur lequel se trouve Yunohost (Debian Jessie dans mon cas)
Avec lIPv6, chaque machine se voit attribuer une IP publique, basée sur lIP principale (celle de la Freebox) et ladresse MAC de léquipement concerné (pour faire simple). Une fois IPv6 activée sur la Freebox, la machine Yunohost étant réglée en DHCP (avec comme paramétrage au niveau de la Freebox de toujours associer la même IP), la machine se voit attribuer 2 IPv6.
Une adresse commençant par fe80:: qui est une adresse locale (fonctionnant uniquement sur le sous-réseau physique local : le switch et le wifi de la Freebox). Une adresse commençant par 2a01:exxx:xxxx:xxxx : qui est une adresse publique (La Freebox ayant ladresse 2a01:exxx:xxxx:xxxx::1)
Pour chaque machine ayant une IPv6, la Freebox ouvre un tunnel vers Internet (indépendamment de la redirection de ports nécessaire pour ladresse en IPv4 qui nest que privée, liée au réseau local. Le NAT et la redirection de ports de lIPv4 publique de la Freebox vers lIPv4 locale de la machine sous Yunohost nest utile quen IPv4). La redirection nest donc plus nécessaire puisque la machine peut être attaquée via le tunnel V6. Comme indiqué, ladresse IPv6 est publique : la machine est donc directement visible et accessible depuis Internet (doù la nécessité de surveiller les logs, de faire les mises à jour, davoir un pare-feu activé, fail2ban... tout ce qui est installé et configuré par défaut dans Yunohost).
On peut voir ces deux adresses via un
sudo ifconfig /all
eth0 Link encap:Ethernet HWaddr 78:xx:xx:xx:xx:xx
inet adr:192.168.0.1 Bcast:192.168.0.255 Masque:255.255.255.0
adr inet6: 2a01:exxx:xxxx:xxxx:b81b/64 Scope:Global
adr inet6: fe80::7a2b:cbff:fea8:b81b/64 Scope:Lien
(...)
Configuration du DNS
Si on a un nom de domaine, par exemple mondomaine.fr, il faut noter ladresse IPv6 publique de la machine sous Yunohost et dans la configuration des entrées DNS, en plus de lentrée correspondant à lassociation de lIPv4 publique de la Freebox (ici représentée par XYZ.XYZ.XYZ.XYZ)
Nom Type Valeur
@ A XYZ.XYZ.XYZ.XYZ
On ajoute une entrée AAAA avec ladresse IPv6 publique de la machine sous Yunohost (ici représentée par 2a01:exxx:xxxx:xxxx:b81b)
Nom Type Valeur
@ A XYZ.XYZ.XYZ.XYZ
@ AAAA 2a01:exxx:xxxx:xxxx:b81b
Validation de laccessibilité en IPv6
Depuis une autre machine, sur un réseau extérieur et pour lequel on a une adresse IPv6, on peut vérifier quun
ping -v6 mondomaine.fr
répond et renvoie bien ladresse IPv6 de la machine Yunohost.
On peut aussi tester un traceroute, ou encore, depuis un navigateur, saisir dans la barre dadresse (avec les crochets, contrairement à une IPv4)
http://[2a01:exxx:xxxx:xxxx:b81b]
et ça doit afficher la même page par défaut que si on va sur lurl http://mondomaine.fr1
Conclusion
Si ça marche, votre machine Yunohost est bien accessible en IPv6 de lextérieur. Pratique quand on souhaite sy connecter depuis un réseau sur lequel on na pas dIPv4 (seulement de lIPv6).
</code></pre></div></div>
<hr />
<p>2a01:e34:ee6a:b270::/64
fe80::beae:c5ff:fe57:150c</p>
<p>root@yanspm:/home/yann# cat /etc/network/interfaces</p>
<h1 id="this-file-describes-the-network-interfaces-available-on-your-system">This file describes the network interfaces available on your system</h1>
<h1 id="and-how-to-activate-them-for-more-information-see-interfaces5">and how to activate them. For more information, see interfaces(5).</h1>
<p>source /etc/network/interfaces.d/*</p>
<h1 id="the-loopback-network-interface">The loopback network interface</h1>
<p>auto lo
iface lo inet loopback</p>
<h1 id="the-primary-network-interface">The primary network interface</h1>
<p>allow-hotplug eth0
iface eth0 inet dhcp</p>
<h1 id="this-is-an-autoconfigured-ipv6-interface">This is an autoconfigured IPv6 interface</h1>
<p>iface eth0 inet6 static
address 2a01:e34:ee6a:b270::1
netmask 64
post-up ip -6 route add default via fe80::beae:c5ff:fe57:150c dev eth0</p>
<p>MODIFICATION</p>
<p>root@yanspm:/home/yann# cat /etc/network/interfaces</p>
<h1 id="this-file-describes-the-network-interfaces-available-on-your-system-1">This file describes the network interfaces available on your system</h1>
<h1 id="and-how-to-activate-them-for-more-information-see-interfaces5-1">and how to activate them. For more information, see interfaces(5).</h1>
<p>source /etc/network/interfaces.d/*</p>
<h1 id="the-loopback-network-interface-1">The loopback network interface</h1>
<p>auto lo
iface lo inet loopback</p>
<h1 id="the-primary-network-interface-1">The primary network interface</h1>
<p>allow-hotplug eth0
iface eth0 inet dhcp</p>
<h1 id="this-is-an-autoconfigured-ipv6-interface-1">This is an autoconfigured IPv6 interface</h1>
<p>iface eth0 inet6 static
address 2a01:e34:ee6a:b270::1
netmask 64
post-up ip -6 route add default via fe80::224:d4ff:fea6:aa20 dev eth0</p>
<p>Il faut ensuite installer le paquet “radvd” et le configurer pour quil annonce le bon préfixe sur le réseau local. Dans le cas de notre exemple, cela donne les commandes suivantes (pensez a remplacer le préfixe par votre préfixe, celui que vous avez noté) :
pi@raspberrypi ~ $ sudo apt-get install radvd</p>
<p>pi@raspberrypi ~ $ sudo cat «EOF &gt;/etc/radvd.conf
interface eth0
{
AdvSendAdvert on;
prefix 2a01:e34:ee6a:b270::/64
{
};
}; <br />
EOF
Une fois cela fait, il faut activer le routage des paquets IPv6 dans Linux.
pi@raspberrypi ~ $ sudo echo “net.ipv6.conf.all.forwarding=1” » /etc/sysctl.conf</p>
<p>A partir de maintenant, si on redémarre la Raspberry Pi, elle va agir en tant que routeur IPv6, et lIPv6 sera fonctionnel sur le réseau LAN, tout comme si on avait tout simplement activé IPv6 sur la Freebox.</p>
<hr />
<p>Adresse IPV6 lien local : fe80::224:d4ff:fea6:aa20
Délégation de prefixe
Attention si vous configurez un Next Hop pour le premier subnet, il ne sera plus annoncé par la Freebox sur votre réseau</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Prefixe : 2a01:e34:ee6a:b270::/64
Next hop :
Prefixe : 2a01:e34:ee6a:b271::/64
Next hop : fe80::beae:c5ff:fe57:150c eth0 shuttle
Prefixe : 2a01:e34:ee6a:b272::/64
Next hop :
Prefixe : 2a01:e34:ee6a:b273::/64
Next hop :
Prefixe : 2a01:e34:ee6a:b274::/64
Next hop :
Prefixe : 2a01:e34:ee6a:b275::/64
Next hop :
Prefixe : 2a01:e34:ee6a:b276::/64
Next hop :
Prefixe : 2a01:e34:ee6a:b277::/64
Next hop :
</code></pre></div></div>
<p>fe80::f:54ff:fe13:2400 wlan0 YanHotSpot<br />
fe80::c2:9ff:fe40:f22b eth0 Olibox<br />
fe80::a00:27ff:fecc:c8ba<br />
fe80::20f:54ff:fe13:24b5</p>
<h1 id="ipv6-freecubiebox-olibox-shuttle">IPV6-freecubiebox-olibox-shuttle</h1>
<h2 id="paramétrage-ipv6-free">Paramétrage IPV6 free</h2>
<p>Le lien <a href="https://utux.fr/index.php?tag/ipv6">https://utux.fr/index.php?tag/ipv6</a></p>
<p>La première chose à faire consiste à se rendre dans linterface de configuration de la freebox grâce à ladresse <mafreebox.free.fr> qui fonctionne même si vous êtes en bridge.</mafreebox.free.fr></p>
<p>Puis rendez-vous dans : <strong>Paramètres</strong> de la Freebox, onglet <strong>Mode avancé</strong>, <strong>Configuration IPv6</strong>.</p>
<p>Adresse IPV6 lien local : fe80::224:d4ff:fea6:aa20
Délégation de prefixe
Attention si vous configurez un Next Hop pour le premier subnet, il ne sera plus annoncé par la Freebox sur votre réseau</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Prefixe : 2a01:e34:ee6a:b270::/64
Next hop :
Prefixe : 2a01:e34:ee6a:b271::/64
Next hop : fe80::beae:c5ff:fe57:150c eth0 shuttle
</code></pre></div></div>
<p>Paraméter le réseau ipv6 sur “shuttle”</p>
<h1 id="this-is-an-autoconfigured-ipv6-interface-2">This is an autoconfigured IPv6 interface</h1>
<p>iface eth0 inet6 static
address 2a01:e34:ee6a:b271::1
netmask 64
post-up ip -6 route add default via fe80::beae:c5ff:fe57:150c dev eth0</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
---
## IPv6 sécurisé sur Freebox Révolution avec une Raspberry Pi
[IPv6 sécurisé sur Freebox Révolution avec une Raspberry Pi](https://www.goudal.net/?p=6)
Posted on October 3, 2015
IPv6 est le protocole du futur Internet. Il existe depuis longtemps, mais la transition massive na toujours pas eu lieu, malgré la pénurie dadresses IPv4.
Free a été le premier FAI grand public à proposer lIPv6 via leur offre Freebox.
Nimporte quel abonné Freebox peut aujourdhui activer lIPv6. La Freebox va alors établir un tunnel 6to4rd pour fournir une connectivité IPv6 sur son LAN, et va se mettre a envoyer des paquets **Router Advertisement** pour annoncer le préfixe (un /64) qui a été alloué aux machines sur le LAN qui savent faire de lIPv6.
Dès lors, toute machine connectée au LAN de la Freebox et sachant parler lIPv6 va pouvoir sauto-assigner une IPv6 “publique” (ayant un scope global) et parler en IPv6 avec le reste du monde.
**Problème** : Puisque chaque machine obtient une adresse IPv6 ayant un scope global, nimporte quel autre machine sur lInternet IPv6 peut communiquer avec tous les devices connectés sur le LAN. La Freebox ne propose strictement aucun moyen de filtrer le traffic IPv6. Il faudrait donc mettre en place un filtrage sur chaque device, sans exception. Sauf quinstaller un firewall sur une machine Windows, Mac ou Linux, cest facile, mais dans certains autres cas, cest tout simplement impossible.
Par exemple, une imprimante réseau qui saurait faire de lIPv6 serait alors potentiellement utilisable par nimporte qui, depuis lautre bout du monde, et il ny a aucun moyen dempêcher cela.
Activer lIPv6 sur sa Freebox est donc un pari risqué. Le nombre dadresses disponibles fait quil est certes très peu probable quon arrive a trouver ladresse IPv6 de votre imprimante. Mais le risque existe bel et bien, et limprimante nest quun exemple pas trop “grave” du problème (au pire, on vous vide le bac de feuilles / la cartouche) car laccès à dautres devices pourrait être beaucoup plus compromettant (Camera IP, Serveur NAS, …).
Que faire, alors ?
Plusieurs solutions existent :
* Ne pas activer lIPv6. Ca peut paraitre bête, mais pour linstant, le commun des mortels na pas vraiment besoin dIPv6. Donc a moins de savoir vraiment ce que lon fait, mieux vaut ne pas lactiver tout simplement.
* Utiliser la Freebox comme modem (en mode Bridge) et avoir un routeur/bridge externe. On trouve plusieurs tutos sur Internet pour faire cela, mais cette solution a plusieurs inconvénients, car lorsque la Freebox est en mode Bridge, on perd un certain nombre de fonctionnalités, dont le Wifi, lUPnP, la Seedbox, la TNT en multiposte, … .(cf: &lt;http://www.free.fr/assistance/5082.html&gt;)
* Conserver la Freebox en mode Routeur, et utiliser un deuxième routeur externe uniquement pour la gestion de lIPv6. Cest cette solution que je vais décrire ici, et pour ce faire, je vais utiliser une Raspberry Pi, car cest une solution qui ne coute pas cher, qui consomme peu et qui peut se brancher facilement à la Freebox. On peut cependant faire la meme chose avec nimporte quel device qui fait tourner Linux en adaptant un peu.
Depuis Freebox OS 2.0, la configuration IPv6 permet de configurer des “next hops” pour 8 prefixes /64 (Autrement dit, chaque abonné Freebox dispose dun /61). De plus, lorsquun next hop est configuré sur le premier prefixe, la Freebox nenvoie plus de Router Advertisements sur le LAN.
Lobjectif est donc de :
* Mettre une Raspberry Pi sur le LAN, et activer IPv6
* Router le premier préfixe vers la Raspberry Pi
* Configurer une adresse de ce préfixe sur linterface LAN de la Raspberry Pi
* Faire en sorte que la Raspberry Pi envoie les Router Advertisements pour ce préfixe sur son interface LAN
* Activer le routage des paquets IPv6
* Mettre en place des règles IP6Tables pour filtrer le traffic IPv6
Tout dabord, il faut donc brancher la Raspberry Pi au LAN de la Freebox. Elle va obtenir une IPv4 automatiquement, grâce au DHCP.
La Raspberry Pi na pas IPv6 actif par défaut. Il faut donc lactiver avec les commandes suivantes :
pi@raspberrypi ~ $ sudo modprobe ipv6
pi@raspberrypi ~ $ sudo echo ipv6 &gt;&gt; /etc/modules
Cela aura pour effet de rajouter une adresse IP de type Link-local a linterface eth0. Cette adresse est automatiquement générée en se basant sur ladresse MAC, elle sera donc toujours la même. Il faut la noter, on en aura besoin plus tard. Dans lexemple ci dessous, il sagit de fe80::ba27:ebff:fe36:cb4e
pi@raspberrypi ~ $ sudo ifconfig eth0
</code></pre></div></div>
<p>eth0 Link encap:Ethernet HWaddr b8:27:eb:36:cb:4e<br />
inet addr:192.168.42.34 Bcast:192.168.42.255 Mask:255.255.255.0
inet6 addr: fe80::ba27:ebff:fe36:cb4e/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:82134 errors:0 dropped:3 overruns:0 frame:0
TX packets:83989 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:31899129 (30.4 MiB) TX bytes:34922782 (33.3 MiB)</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
* il faut activer IPv6 dans linterface web de la Freebox, et configurer le Next Hop du premier préfixe avec ladresse IP Link-local de la Raspberry Pi que lon a notée plus haut.
* Il faut également noter ladresse IPv6 lien local de la Freebox (ici fe80::f6ca:e5ff:fe57:1ceb) ainsi que le préfixe (ici 2001:db8:dead:beef::/64). Le préfixe utilisé ici à été modifié pour rédiger ce tutoriel, nessayez pas dy accéder, cest une IP qui nexiste pas vraiment.
![Screen-Shot-2015-09-27-at-23](file://media/1880561980.png)
Il est possible que ladresse IPv6 lien local ne saffiche pas, juste après avoir activé lIPv6. Il suffit de fermer et ouvrir la page de configuration a nouveau pour la voir safficher.
Il est bien important que ce soit le premier préfixe pour lequel on spécifie le next hop. Autrement, la Freebox va continuer denvoyer des Router Advertisements sur le LAN, et le traffic IPv6 risque donc de passer directement par la Freebox, sans passer par la Raspberry Pi, et donc sans filtrage.
A partir de maintenant, on a donc un prefixe IPv6 public qui est routé de la Freebox vers la Raspberry Pi (a travers son IP Link-local sur le LAN).
Passons maintenant a la configuration IPv6 de la Raspberry Pi.
On va assigner la premiere adresse IPv6 du préfixe a linterface eth0, et rajouter une route par défaut vers ladresse IPv6 Link-local de la Freebox. Dans le cas de notre exemple, il faut donc executer la commande suivante. Pensez donc a remplacer les deux adresses IPv6 par celles notées à létape prédédente (pour construire ladresse IPv6 de linterface, il faut simplement retirer le “/64″ a la fin du préfixe, et le remplacer par “1”) :
</code></pre></div></div>
<p>sudo cat «EOF »/etc/network/interfaces
iface eth0 inet6 static
address 2001:db8:dead:beef::1
netmask 64
post-up ip -6 route add default via fe80::f6ca:e5ff:fe57:1ceb dev eth0
EOF</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
Il faut ensuite installer le paquet “radvd” et le configurer pour quil annonce le bon préfixe sur le réseau local. Dans le cas de notre exemple, cela donne les commandes suivantes (pensez a remplacer le préfixe par votre préfixe, celui que vous avez noté) :
pi@raspberrypi ~ $ sudo apt-get install radvd
</code></pre></div></div>
<p>sudo cat «EOF &gt;/etc/radvd.conf
interface eth0
{
AdvSendAdvert on;
prefix 2001:db8:dead:beef::/64
{
};
}; <br />
EOF</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
Une fois cela fait, il faut activer le routage des paquets IPv6 dans Linux.
pi@raspberrypi ~ $ sudo echo "net.ipv6.conf.all.forwarding=1" &gt;&gt; /etc/sysctl.conf
A partir de maintenant, si on redémarre la Raspberry Pi, elle va agir en tant que routeur IPv6, et lIPv6 sera fonctionnel sur le réseau LAN, tout comme si on avait tout simplement activé IPv6 sur la Freebox. Mais le problème que lon souhaitait résoudre a la base sera présent, puisquaucun filtrage nest en place. Donc toutes les machines sur le LAN qui savent faire de lIPv6 seront directement joignables depuis le reste de lInternet IPv6.
Il nous reste donc a mettre en place un filtrage basique. Lidée est de retomber dans le même genre de protection que celle fournie implicitement en IPv4 par le NAT. A savoir, les machines vers le LAN peuvent accéder a toute machine vers lInternet, mais linverse nest pas possible. Pour cela, on crée un fichier de configuration pour ip6tables (le firewall IPv6 de Linux) :
</code></pre></div></div>
<p>sudo cat «EOF &gt;/etc/ip6tables.conf
*filter
:INPUT ACCEPT [14:960]
:FORWARD DROP [34:3344]
:OUTPUT ACCEPT [63:15248]
-A FORWARD -s 2001:db8:dead:beef::/64 -j ACCEPT
-A FORWARD -m state state RELATED,ESTABLISHED -j ACCEPT
COMMIT
EOF</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
Encore une fois, noubliez pas dadapter la commande a votre préfixe. Ce que lon fait ici cest quon va refuser de router les paquets qui viennent de linternet vers notre LAN, sauf si on est capable de les associer a une session qui a été initiée depuis notre LAN vers lInternet.
Reste maintenant a faire en sorte que ces règles soient mises en place au démarrage :
</code></pre></div></div>
<p>pi@raspberrypi ~ $ sudo cat «EOF »/etc/network/interfaces
post-up ip6tables-restore &lt; /etc/ip6tables.conf
EOF</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
Puis a rebooter la Raspberry Pi afin dappliquer tous ces nouveaux paramètres.
pi@raspberrypi ~ $ sudo reboot
Voila, cest terminé !
Si tout se passe bien, vos machines sur le LAN devraient maintenant recevoir un router advertisement IPv6 de la Raspberry Pi, et donc sauto-assigner une adresse IPv6 globale, et router leur traffic IPv6 vers la Raspberry Pi, qui va le faire suivre a la Freebox.
Vous pouvez verifier que tout fonctionne en allant visiter le site http://test-ipv6.com/ qui devrait normalement vous retourner un score de 10/10.
This entry was posted in Freebox, IPv6, Linux, Networking, Raspberry Pi by Fanfwe. Bookmark the permalink.
## OpenVpn carte Olimex + MultiWebApp
Installation
sudo apt install openvpn
Configuration **/etc/openvpn/vpntest.conf**
Bash pour activer le VPN
sudo nano /usr/local/bin/startvpn
</code></pre></div></div>
<p>#!/bin/bash</p>
<h1 id="lancement-openvpn">Lancement openvpn</h1>
<p>if [ $(systemctl is-active openvpn@vpntest &gt;/dev/null 2&gt;&amp;1 &amp;&amp; echo true || echo false) = false ]; then
echo “lancer openvpn et attendre 5 secondes …”
sudo systemctl start openvpn@vpntest
# attendre 5 secondes
sleep 5
else
echo “démarré”
exit 0
fi</p>
<h1 id="règles-iptables">règles iptables</h1>
<p>if [ ! -e “/tmp/iptablesvpn” ]; then
echo “ajout des règles iptables”
sudo iptables -w -t nat -A POSTROUTING -o tun0 -j MASQUERADE -m comment comment “Use VPN IP for tun0”<br />
#sudo iptables -w -A FORWARD -s 10.118.111.0/24 -i wlan0 -o tun0 -m conntrack ctstate NEW -j ACCEPT -m comment comment “Allow only traffic from wlan0 clients to tun0”
#sudo iptables -w -A FORWARD -s 192.168.0.0/24 -i eth0 -o tun0 -m conntrack ctstate NEW -j ACCEPT -m comment comment “Allow only traffic from eth0 clients to tun0”<br />
# on a ajouté les règles iptables
touch /tmp/iptablesvpn
else
echo “règles iptables existantes”
fi</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
Droits en exécution
sudo chmod +x /usr/local/bin/startvpn
Démarrage
startvpn
Vérification
</code></pre></div></div>
<p>debian@olibox:~$ ip link |grep tun
6: tun0: &lt;POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP&gt; mtu 1500 qdisc pfifo_fast state UNKNOWN mode DEFAULT group default qlen 100</p>
<p>debian@olibox:~$ ip route
0.0.0.0/1 via 10.8.0.1 dev tun0
default via 192.168.0.254 dev eth0
10.0.242.0/24 dev wlan0 proto kernel scope link src 10.0.242.1
10.8.0.0/24 dev tun0 proto kernel scope link src 10.8.0.2
10.118.111.0/24 dev hotspot2 proto kernel scope link src 10.118.111.1
10.231.51.0/24 dev hotspot1 proto kernel scope link src 10.231.51.1
37.235.49.24 via 192.168.0.254 dev eth0
128.0.0.0/1 via 10.8.0.1 dev tun0
192.168.0.0/24 dev eth0 proto kernel scope link src 192.168.0.43</p>
<h1 id="règles">règles</h1>
<p>$ sudo iptables -L -t nat</p>
<p>MASQUERADE all anywhere anywhere /* Use VPN IP for tun0 */</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
#### Multi custom webapp (multi_webapp)
**multi_webapp** : &lt;https://github.com/YunoHost-Apps/multi_webapp_ynh&gt;
App vide sans accès FTP
Dossier : /me
Publique : non
Modification configuration **/etc/php5/fpm/pool.d/webapp_me.conf** pour autoriser le *php exec*
</code></pre></div></div>
<p>[webapp_me]</p>
<p>listen = /var/run/php5-fpm-webapp_me.sock</p>
<p>listen.owner = www-data
listen.group = www-data
listen.mode = 0600</p>
<p>user = admin
group = admins</p>
<p>pm = dynamic
pm.max_children = 6
pm.start_servers = 3
pm.min_spare_servers = 3
pm.max_spare_servers = 5
pm.max_requests = 500
pm.status_path = /fpm-status</p>
<p>ping.path = /ping</p>
<p>request_terminate_timeout = 600s</p>
<p>request_slowlog_timeout = 0</p>
<p>slowlog = /var/log/nginx/webapp_me.slow.log</p>
<p>rlimit_files = 4096</p>
<p>rlimit_core = 0</p>
<p>chdir = /var/www/webapp_yan/me</p>
<p>catch_workers_output = no</p>
<p>php_value[max_execution_time] = 600
php_value[upload_max_filesize] = 10G
php_value[post_max_size] = 10G</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
Relancer php5-fpm
sudo systemctl restart php5-fpm
Le fichier de configuration nginx
/etc/nginx/conf.d/yan.me.d/webapp_me.conf
</code></pre></div></div>
<p>location /me {
alias /var/www/webapp_yan/me/ ;
if ($scheme = http) {
rewrite ^ https://$server_name$request_uri? permanent;
}
index index.html index.php ;
try_files $uri $uri/ index.php;
location ~ [^/].php(/|$) {
fastcgi_split_path_info ^(.+?.php)(/.*)$;
fastcgi_pass unix:/var/run/php5-fpm-webapp_me.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param REMOTE_USER $remote_user;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param SCRIPT_FILENAME $request_filename;
}</p>
<p># Include SSOWAT user panel.
include conf.d/yunohost_panel.conf.inc;
}</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
On implémente le micro framework php limonade
Structure du dossier
tree -L 4 /var/www/webapp_yan/me/
</code></pre></div></div>
<p>/var/www/webapp_yan/me/
├── config.php
├── entries
├── index.php
├── lib
│ ├── limonade
│ ├── limonade.php
│ ├── vendors
│ │ └── markdown
│ │ ├── markdown.php
│ │ └── PHP Markdown Readme.text
│ └── vendors.php
├── pages
├── public
│ ├── bootstrap
│ │ ├── css
│ │ │ ├── bootstrap.css
│ │ │ ├── bootstrap.css.map
│ │ │ ├── bootstrap.min.css
│ │ │ ├── bootstrap-theme.css
│ │ │ ├── bootstrap-theme.css.map
│ │ │ └── bootstrap-theme.min.css
│ │ ├── fonts
│ │ │ ├── glyphicons-halflings-regular.eot
│ │ │ ├── glyphicons-halflings-regular.svg
│ │ │ ├── glyphicons-halflings-regular.ttf
│ │ │ └── glyphicons-halflings-regular.woff
│ │ └── js
│ │ ├── bootstrap.js
│ │ ├── bootstrap.min.js
│ │ └── npm.js
│ ├── css
│ │ ├── bootstrap-toggle.min.css
│ │ ├── screen.css
│ │ └── style.css
│ ├── img
│ │ ├── A20-Olinuxino-MICRO.png
│ │ ├── bg_header_wikir.jpg
│ │ ├── github.png
│ │ ├── loading.gif
│ │ └── nice.ogg
│ ├── jquery
│ │ └── jquery-2.1.1.min.js
│ └── js
│ ├── bootstrap-toggle.min.js
│ └── custom.js
└── views
├── about.html.php
├── entry.html.php
├── index.html.php
├── layout.html.php
└── posts.html.php
```</p>
<p>Accès web <a href="https://yan.me/me/">https://yan.me/me/</a></p>
</div>
<div class="d-print-none"><footer class="article__footer"><meta itemprop="dateModified" content="2019-12-25T00:00:00+01:00"><!-- start custom article footer snippet -->
<!-- end custom article footer snippet -->
<!--
<div align="right"><a type="application/rss+xml" href="/feed.xml" title="S'abonner"><i class="fa fa-rss fa-2x"></i></a>
&emsp;</div>
-->
</footer>
<div class="article__section-navigator clearfix"><div class="previous"><span>PRÉCÉDENT</span><a href="/2019/12/25/firstheberg-ip-failover.html">firstheberg-ip-failover</a></div><div class="next"><span>SUIVANT</span><a href="/2019/12/25/geoloc.html">geoloc</a></div></div></div>
</div>
<script>(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
$(function() {
var $this ,$scroll;
var $articleContent = $('.js-article-content');
var hasSidebar = $('.js-page-root').hasClass('layout--page--sidebar');
var scroll = hasSidebar ? '.js-page-main' : 'html, body';
$scroll = $(scroll);
$articleContent.find('.highlight').each(function() {
$this = $(this);
$this.attr('data-lang', $this.find('code').attr('data-lang'));
});
$articleContent.find('h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]').each(function() {
$this = $(this);
$this.append($('<a class="anchor d-print-none" aria-hidden="true"></a>').html('<i class="fas fa-anchor"></i>'));
});
$articleContent.on('click', '.anchor', function() {
$scroll.scrollToAnchor('#' + $(this).parent().attr('id'), 400);
});
});
});
})();
</script>
</div><section class="page__comments d-print-none"></section></article><!-- start custom main bottom snippet -->
<!-- end custom main bottom snippet -->
</div>
</div></div></div></div>
</div><script>(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
var $body = $('body'), $window = $(window);
var $pageRoot = $('.js-page-root'), $pageMain = $('.js-page-main');
var activeCount = 0;
function modal(options) {
var $root = this, visible, onChange, hideWhenWindowScroll = false;
var scrollTop;
function setOptions(options) {
var _options = options || {};
visible = _options.initialVisible === undefined ? false : show;
onChange = _options.onChange;
hideWhenWindowScroll = _options.hideWhenWindowScroll;
}
function init() {
setState(visible);
}
function setState(isShow) {
if (isShow === visible) {
return;
}
visible = isShow;
if (visible) {
activeCount++;
scrollTop = $(window).scrollTop() || $pageMain.scrollTop();
$root.addClass('modal--show');
$pageMain.scrollTop(scrollTop);
activeCount === 1 && ($pageRoot.addClass('show-modal'), $body.addClass('of-hidden'));
hideWhenWindowScroll && window.hasEvent('touchstart') && $window.on('scroll', hide);
$window.on('keyup', handleKeyup);
} else {
activeCount > 0 && activeCount--;
$root.removeClass('modal--show');
$window.scrollTop(scrollTop);
activeCount === 0 && ($pageRoot.removeClass('show-modal'), $body.removeClass('of-hidden'));
hideWhenWindowScroll && window.hasEvent('touchstart') && $window.off('scroll', hide);
$window.off('keyup', handleKeyup);
}
onChange && onChange(visible);
}
function show() {
setState(true);
}
function hide() {
setState(false);
}
function handleKeyup(e) {
// Char Code: 27 ESC
if (e.which === 27) {
hide();
}
}
setOptions(options);
init();
return {
show: show,
hide: hide,
$el: $root
};
}
$.fn.modal = modal;
});
})();
</script><div class="modal modal--overflow page__search-modal d-print-none js-page-search-modal"><script>
(function () {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
// search panel
var search = (window.search || (window.search = {}));
var useDefaultSearchBox = window.useDefaultSearchBox === undefined ?
true : window.useDefaultSearchBox ;
var $searchModal = $('.js-page-search-modal');
var $searchToggle = $('.js-search-toggle');
var searchModal = $searchModal.modal({ onChange: handleModalChange, hideWhenWindowScroll: true });
var modalVisible = false;
search.searchModal = searchModal;
var $searchBox = null;
var $searchInput = null;
var $searchClear = null;
function getModalVisible() {
return modalVisible;
}
search.getModalVisible = getModalVisible;
function handleModalChange(visible) {
modalVisible = visible;
if (visible) {
search.onShow && search.onShow();
useDefaultSearchBox && $searchInput[0] && $searchInput[0].focus();
} else {
search.onShow && search.onHide();
useDefaultSearchBox && $searchInput[0] && $searchInput[0].blur();
setTimeout(function() {
useDefaultSearchBox && ($searchInput.val(''), $searchBox.removeClass('not-empty'));
search.clear && search.clear();
window.pageAsideAffix && window.pageAsideAffix.refresh();
}, 400);
}
}
$searchToggle.on('click', function() {
modalVisible ? searchModal.hide() : searchModal.show();
});
// Char Code: 83 S, 191 /
$(window).on('keyup', function(e) {
if (!modalVisible && !window.isFormElement(e.target || e.srcElement) && (e.which === 83 || e.which === 191)) {
modalVisible || searchModal.show();
}
});
if (useDefaultSearchBox) {
$searchBox = $('.js-search-box');
$searchInput = $searchBox.children('input');
$searchClear = $searchBox.children('.js-icon-clear');
search.getSearchInput = function() {
return $searchInput.get(0);
};
search.getVal = function() {
return $searchInput.val();
};
search.setVal = function(val) {
$searchInput.val(val);
};
$searchInput.on('focus', function() {
$(this).addClass('focus');
});
$searchInput.on('blur', function() {
$(this).removeClass('focus');
});
$searchInput.on('input', window.throttle(function() {
var val = $(this).val();
if (val === '' || typeof val !== 'string') {
search.clear && search.clear();
} else {
$searchBox.addClass('not-empty');
search.onInputNotEmpty && search.onInputNotEmpty(val);
}
}, 400));
$searchClear.on('click', function() {
$searchInput.val(''); $searchBox.removeClass('not-empty');
search.clear && search.clear();
});
}
});
})();
</script><div class="search search--dark">
<div class="main">
<div class="search__header">Recherche</div>
<div class="search-bar">
<div class="search-box js-search-box">
<div class="search-box__icon-search"><i class="fas fa-search"></i></div>
<input id="search-input" type="text" />
<div class="search-box__icon-clear js-icon-clear">
<a><i class="fas fa-times"></i></a>
</div>
</div>
<button class="button button--theme-dark button--pill search__cancel js-search-toggle">
Annuler</button>
</div>
<div id="results-container" class="search-result js-search-result"></div>
</div>
</div>
<!-- Script pointing to search-script.js -->
<script>/*!
* Simple-Jekyll-Search
* Copyright 2015-2020, Christian Fei
* Licensed under the MIT License.
*/
(function(){
'use strict'
var _$Templater_7 = {
compile: compile,
setOptions: setOptions
}
const options = {}
options.pattern = /\{(.*?)\}/g
options.template = ''
options.middleware = function () {}
function setOptions (_options) {
options.pattern = _options.pattern || options.pattern
options.template = _options.template || options.template
if (typeof _options.middleware === 'function') {
options.middleware = _options.middleware
}
}
function compile (data) {
return options.template.replace(options.pattern, function (match, prop) {
const value = options.middleware(prop, data[prop], options.template)
if (typeof value !== 'undefined') {
return value
}
return data[prop] || match
})
}
'use strict';
function fuzzysearch (needle, haystack) {
var tlen = haystack.length;
var qlen = needle.length;
if (qlen > tlen) {
return false;
}
if (qlen === tlen) {
return needle === haystack;
}
outer: for (var i = 0, j = 0; i < qlen; i++) {
var nch = needle.charCodeAt(i);
while (j < tlen) {
if (haystack.charCodeAt(j++) === nch) {
continue outer;
}
}
return false;
}
return true;
}
var _$fuzzysearch_1 = fuzzysearch;
'use strict'
/* removed: const _$fuzzysearch_1 = require('fuzzysearch') */;
var _$FuzzySearchStrategy_5 = new FuzzySearchStrategy()
function FuzzySearchStrategy () {
this.matches = function (string, crit) {
return _$fuzzysearch_1(crit.toLowerCase(), string.toLowerCase())
}
}
'use strict'
var _$LiteralSearchStrategy_6 = new LiteralSearchStrategy()
function LiteralSearchStrategy () {
this.matches = function (str, crit) {
if (!str) return false
str = str.trim().toLowerCase()
crit = crit.trim().toLowerCase()
return crit.split(' ').filter(function (word) {
return str.indexOf(word) >= 0
}).length === crit.split(' ').length
}
}
'use strict'
var _$Repository_4 = {
put: put,
clear: clear,
search: search,
setOptions: __setOptions_4
}
/* removed: const _$FuzzySearchStrategy_5 = require('./SearchStrategies/FuzzySearchStrategy') */;
/* removed: const _$LiteralSearchStrategy_6 = require('./SearchStrategies/LiteralSearchStrategy') */;
function NoSort () {
return 0
}
const data = []
let opt = {}
opt.fuzzy = false
opt.limit = 10
opt.searchStrategy = opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6
opt.sort = NoSort
opt.exclude = []
function put (data) {
if (isObject(data)) {
return addObject(data)
}
if (isArray(data)) {
return addArray(data)
}
return undefined
}
function clear () {
data.length = 0
return data
}
function isObject (obj) {
return Boolean(obj) && Object.prototype.toString.call(obj) === '[object Object]'
}
function isArray (obj) {
return Boolean(obj) && Object.prototype.toString.call(obj) === '[object Array]'
}
function addObject (_data) {
data.push(_data)
return data
}
function addArray (_data) {
const added = []
clear()
for (let i = 0, len = _data.length; i < len; i++) {
if (isObject(_data[i])) {
added.push(addObject(_data[i]))
}
}
return added
}
function search (crit) {
if (!crit) {
return []
}
return findMatches(data, crit, opt.searchStrategy, opt).sort(opt.sort)
}
function __setOptions_4 (_opt) {
opt = _opt || {}
opt.fuzzy = _opt.fuzzy || false
opt.limit = _opt.limit || 10
opt.searchStrategy = _opt.fuzzy ? _$FuzzySearchStrategy_5 : _$LiteralSearchStrategy_6
opt.sort = _opt.sort || NoSort
opt.exclude = _opt.exclude || []
}
function findMatches (data, crit, strategy, opt) {
const matches = []
for (let i = 0; i < data.length && matches.length < opt.limit; i++) {
const match = findMatchesInObject(data[i], crit, strategy, opt)
if (match) {
matches.push(match)
}
}
return matches
}
function findMatchesInObject (obj, crit, strategy, opt) {
for (const key in obj) {
if (!isExcluded(obj[key], opt.exclude) && strategy.matches(obj[key], crit)) {
return obj
}
}
}
function isExcluded (term, excludedTerms) {
for (let i = 0, len = excludedTerms.length; i < len; i++) {
const excludedTerm = excludedTerms[i]
if (new RegExp(excludedTerm).test(term)) {
return true
}
}
return false
}
/* globals ActiveXObject:false */
'use strict'
var _$JSONLoader_2 = {
load: load
}
function load (location, callback) {
const xhr = getXHR()
xhr.open('GET', location, true)
xhr.onreadystatechange = createStateChangeListener(xhr, callback)
xhr.send()
}
function createStateChangeListener (xhr, callback) {
return function () {
if (xhr.readyState === 4 && xhr.status === 200) {
try {
callback(null, JSON.parse(xhr.responseText))
} catch (err) {
callback(err, null)
}
}
}
}
function getXHR () {
return window.XMLHttpRequest ? new window.XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP')
}
'use strict'
var _$OptionsValidator_3 = function OptionsValidator (params) {
if (!validateParams(params)) {
throw new Error('-- OptionsValidator: required options missing')
}
if (!(this instanceof OptionsValidator)) {
return new OptionsValidator(params)
}
const requiredOptions = params.required
this.getRequiredOptions = function () {
return requiredOptions
}
this.validate = function (parameters) {
const errors = []
requiredOptions.forEach(function (requiredOptionName) {
if (typeof parameters[requiredOptionName] === 'undefined') {
errors.push(requiredOptionName)
}
})
return errors
}
function validateParams (params) {
if (!params) {
return false
}
return typeof params.required !== 'undefined' && params.required instanceof Array
}
}
'use strict'
var _$utils_9 = {
merge: merge,
isJSON: isJSON
}
function merge (defaultParams, mergeParams) {
const mergedOptions = {}
for (const option in defaultParams) {
mergedOptions[option] = defaultParams[option]
if (typeof mergeParams[option] !== 'undefined') {
mergedOptions[option] = mergeParams[option]
}
}
return mergedOptions
}
function isJSON (json) {
try {
if (json instanceof Object && JSON.parse(JSON.stringify(json))) {
return true
}
return false
} catch (err) {
return false
}
}
var _$src_8 = {};
(function (window) {
'use strict'
let options = {
searchInput: null,
resultsContainer: null,
json: [],
success: Function.prototype,
searchResultTemplate: '<li><a href="{url}" title="{desc}">{title}</a></li>',
templateMiddleware: Function.prototype,
sortMiddleware: function () {
return 0
},
noResultsText: 'No results found',
limit: 10,
fuzzy: false,
debounceTime: null,
exclude: []
}
let debounceTimerHandle
const debounce = function (func, delayMillis) {
if (delayMillis) {
clearTimeout(debounceTimerHandle)
debounceTimerHandle = setTimeout(func, delayMillis)
} else {
func.call()
}
}
const requiredOptions = ['searchInput', 'resultsContainer', 'json']
/* removed: const _$Templater_7 = require('./Templater') */;
/* removed: const _$Repository_4 = require('./Repository') */;
/* removed: const _$JSONLoader_2 = require('./JSONLoader') */;
const optionsValidator = _$OptionsValidator_3({
required: requiredOptions
})
/* removed: const _$utils_9 = require('./utils') */;
window.SimpleJekyllSearch = function (_options) {
const errors = optionsValidator.validate(_options)
if (errors.length > 0) {
throwError('You must specify the following required options: ' + requiredOptions)
}
options = _$utils_9.merge(options, _options)
_$Templater_7.setOptions({
template: options.searchResultTemplate,
middleware: options.templateMiddleware
})
_$Repository_4.setOptions({
fuzzy: options.fuzzy,
limit: options.limit,
sort: options.sortMiddleware,
exclude: options.exclude
})
if (_$utils_9.isJSON(options.json)) {
initWithJSON(options.json)
} else {
initWithURL(options.json)
}
const rv = {
search: search
}
typeof options.success === 'function' && options.success.call(rv)
return rv
}
function initWithJSON (json) {
_$Repository_4.put(json)
registerInput()
}
function initWithURL (url) {
_$JSONLoader_2.load(url, function (err, json) {
if (err) {
throwError('failed to get JSON (' + url + ')')
}
initWithJSON(json)
})
}
function emptyResultsContainer () {
options.resultsContainer.innerHTML = ''
}
function appendToResultsContainer (text) {
options.resultsContainer.innerHTML += text
}
function registerInput () {
options.searchInput.addEventListener('input', function (e) {
if (isWhitelistedKey(e.which)) {
emptyResultsContainer()
debounce(function () { search(e.target.value) }, options.debounceTime)
}
})
}
function search (query) {
if (isValidQuery(query)) {
emptyResultsContainer()
render(_$Repository_4.search(query), query)
}
}
function render (results, query) {
const len = results.length
if (len === 0) {
return appendToResultsContainer(options.noResultsText)
}
for (let i = 0; i < len; i++) {
results[i].query = query
appendToResultsContainer(_$Templater_7.compile(results[i]))
}
}
function isValidQuery (query) {
return query && query.length > 0
}
function isWhitelistedKey (key) {
return [13, 16, 20, 37, 38, 39, 40, 91].indexOf(key) === -1
}
function throwError (message) {
throw new Error('SimpleJekyllSearch --- ' + message)
}
})(window)
}());
</script>
<!-- Configuration -->
<script>
SimpleJekyllSearch({
searchInput: document.getElementById('search-input'),
resultsContainer: document.getElementById('results-container'),
noResultsText: '<p>Aucun résultat!</p>',
json: '/search.json',
searchResultTemplate: '<li><a href="{url}">{date}&nbsp;{title}</a>&nbsp;(Création {create})</li>'
})
</script>
</div></div>
<script>(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
function scrollToAnchor(anchor, duration, callback) {
var $root = this;
$root.animate({ scrollTop: $(anchor).position().top }, duration, function() {
window.history.replaceState(null, '', window.location.href.split('#')[0] + anchor);
callback && callback();
});
}
$.fn.scrollToAnchor = scrollToAnchor;
});
})();
(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
function affix(options) {
var $root = this, $window = $(window), $scrollTarget, $scroll,
offsetBottom = 0, scrollTarget = window, scroll = window.document, disabled = false, isOverallScroller = true,
rootTop, rootLeft, rootHeight, scrollBottom, rootBottomTop,
hasInit = false, curState;
function setOptions(options) {
var _options = options || {};
_options.offsetBottom && (offsetBottom = _options.offsetBottom);
_options.scrollTarget && (scrollTarget = _options.scrollTarget);
_options.scroll && (scroll = _options.scroll);
_options.disabled !== undefined && (disabled = _options.disabled);
$scrollTarget = $(scrollTarget);
isOverallScroller = window.isOverallScroller($scrollTarget[0]);
$scroll = $(scroll);
}
function preCalc() {
top();
rootHeight = $root.outerHeight();
rootTop = $root.offset().top + (isOverallScroller ? 0 : $scrollTarget.scrollTop());
rootLeft = $root.offset().left;
}
function calc(needPreCalc) {
needPreCalc && preCalc();
scrollBottom = $scroll.outerHeight() - offsetBottom - rootHeight;
rootBottomTop = scrollBottom - rootTop;
}
function top() {
if (curState !== 'top') {
$root.removeClass('fixed').css({
left: 0,
top: 0
});
curState = 'top';
}
}
function fixed() {
if (curState !== 'fixed') {
$root.addClass('fixed').css({
left: rootLeft + 'px',
top: 0
});
curState = 'fixed';
}
}
function bottom() {
if (curState !== 'bottom') {
$root.removeClass('fixed').css({
left: 0,
top: rootBottomTop + 'px'
});
curState = 'bottom';
}
}
function setState() {
var scrollTop = $scrollTarget.scrollTop();
if (scrollTop >= rootTop && scrollTop <= scrollBottom) {
fixed();
} else if (scrollTop < rootTop) {
top();
} else {
bottom();
}
}
function init() {
if(!hasInit) {
var interval, timeout;
calc(true); setState();
// run calc every 100 millisecond
interval = setInterval(function() {
calc();
}, 100);
timeout = setTimeout(function() {
clearInterval(interval);
}, 45000);
window.pageLoad.then(function() {
setTimeout(function() {
clearInterval(interval);
clearTimeout(timeout);
}, 3000);
});
$scrollTarget.on('scroll', function() {
disabled || setState();
});
$window.on('resize', function() {
disabled || (calc(true), setState());
});
hasInit = true;
}
}
setOptions(options);
if (!disabled) {
init();
}
$window.on('resize', window.throttle(function() {
init();
}, 200));
return {
setOptions: setOptions,
refresh: function() {
calc(true, { animation: false }); setState();
}
};
}
$.fn.affix = affix;
});
})();
(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
function toc(options) {
var $root = this, $window = $(window), $scrollTarget, $scroller, $tocUl = $('<ul class="toc toc--ellipsis"></ul>'), $tocLi, $headings, $activeLast, $activeCur,
selectors = 'h1,h2,h3', container = 'body', scrollTarget = window, scroller = 'html, body', disabled = false,
headingsPos, scrolling = false, hasRendered = false, hasInit = false;
function setOptions(options) {
var _options = options || {};
_options.selectors && (selectors = _options.selectors);
_options.container && (container = _options.container);
_options.scrollTarget && (scrollTarget = _options.scrollTarget);
_options.scroller && (scroller = _options.scroller);
_options.disabled !== undefined && (disabled = _options.disabled);
$headings = $(container).find(selectors).filter('[id]');
$scrollTarget = $(scrollTarget);
$scroller = $(scroller);
}
function calc() {
headingsPos = [];
$headings.each(function() {
headingsPos.push(Math.floor($(this).position().top));
});
}
function setState(element, disabled) {
var scrollTop = $scrollTarget.scrollTop(), i;
if (disabled || !headingsPos || headingsPos.length < 1) { return; }
if (element) {
$activeCur = element;
} else {
for (i = 0; i < headingsPos.length; i++) {
if (scrollTop >= headingsPos[i]) {
$activeCur = $tocLi.eq(i);
} else {
$activeCur || ($activeCur = $tocLi.eq(i));
break;
}
}
}
$activeLast && $activeLast.removeClass('active');
($activeLast = $activeCur).addClass('active');
}
function render() {
if(!hasRendered) {
$root.append($tocUl);
$headings.each(function() {
var $this = $(this);
$tocUl.append($('<li></li>').addClass('toc-' + $this.prop('tagName').toLowerCase())
.append($('<a></a>').text($this.text()).attr('href', '#' + $this.prop('id'))));
});
$tocLi = $tocUl.children('li');
$tocUl.on('click', 'a', function(e) {
e.preventDefault();
var $this = $(this);
scrolling = true;
setState($this.parent());
$scroller.scrollToAnchor($this.attr('href'), 400, function() {
scrolling = false;
});
});
}
hasRendered = true;
}
function init() {
var interval, timeout;
if(!hasInit) {
render(); calc(); setState(null, scrolling);
// run calc every 100 millisecond
interval = setInterval(function() {
calc();
}, 100);
timeout = setTimeout(function() {
clearInterval(interval);
}, 45000);
window.pageLoad.then(function() {
setTimeout(function() {
clearInterval(interval);
clearTimeout(timeout);
}, 3000);
});
$scrollTarget.on('scroll', function() {
disabled || setState(null, scrolling);
});
$window.on('resize', window.throttle(function() {
if (!disabled) {
render(); calc(); setState(null, scrolling);
}
}, 100));
}
hasInit = true;
}
setOptions(options);
if (!disabled) {
init();
}
$window.on('resize', window.throttle(function() {
init();
}, 200));
return {
setOptions: setOptions
};
}
$.fn.toc = toc;
});
})();
/*(function () {
})();*/
</script><script>
/* toc must before affix, since affix need to konw toc' height. */(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
var TOC_SELECTOR = window.TEXT_VARIABLES.site.toc.selectors;
window.Lazyload.js(SOURCES.jquery, function() {
var $window = $(window);
var $articleContent = $('.js-article-content');
var $tocRoot = $('.js-toc-root'), $col2 = $('.js-col-aside');
var toc;
var tocDisabled = false;
var hasSidebar = $('.js-page-root').hasClass('layout--page--sidebar');
var hasToc = $articleContent.find(TOC_SELECTOR).length > 0;
function disabled() {
return $col2.css('display') === 'none' || !hasToc;
}
tocDisabled = disabled();
toc = $tocRoot.toc({
selectors: TOC_SELECTOR,
container: $articleContent,
scrollTarget: hasSidebar ? '.js-page-main' : null,
scroller: hasSidebar ? '.js-page-main' : null,
disabled: tocDisabled
});
$window.on('resize', window.throttle(function() {
tocDisabled = disabled();
toc && toc.setOptions({
disabled: tocDisabled
});
}, 100));
});
})();
(function() {
var SOURCES = window.TEXT_VARIABLES.sources;
window.Lazyload.js(SOURCES.jquery, function() {
var $window = $(window), $pageFooter = $('.js-page-footer');
var $pageAside = $('.js-page-aside');
var affix;
var tocDisabled = false;
var hasSidebar = $('.js-page-root').hasClass('layout--page--sidebar');
affix = $pageAside.affix({
offsetBottom: $pageFooter.outerHeight(),
scrollTarget: hasSidebar ? '.js-page-main' : null,
scroller: hasSidebar ? '.js-page-main' : null,
scroll: hasSidebar ? $('.js-page-main').children() : null,
disabled: tocDisabled
});
$window.on('resize', window.throttle(function() {
affix && affix.setOptions({
disabled: tocDisabled
});
}, 100));
window.pageAsideAffix = affix;
});
})();
</script><!---->
</div>
<script>(function () {
var $root = document.getElementsByClassName('root')[0];
if (window.hasEvent('touchstart')) {
$root.dataset.isTouch = true;
document.addEventListener('touchstart', function(){}, false);
}
})();
</script>
</body>
</html>